本篇文章汇总了,一些typescript的基础概念和一些常用的类型工具方法。
常见的typescript类型定义
const num: number = 123 // 数值const str: string = 'string' // 字符串const boo: boolean = true // 布尔const un: undefined = undefined // undefinedconst nu: null = null // nullconst obj: object = {a: 1} // 对象const big: bigInt = 100n; // 大数const sym: symbol = Symbol('s') // symbolconst arr: number[] = [1,2,3] // 数组const arr: Array<number> = [1,2,3] // 数组
元组
在一个数组中,定义不同类型的值。
// 定义数组的第一个值是number类型,第二个是string类型const x: [number, string] = [123, 'hello workd']// 解构const [a,b] = xconsole.log(a,b) // 123, hello world
unknown
不可预先定义的类型,和any很相似,但是unknow比any更加安全。
any是任意类型的父类型,同时也是任意类型的子类型。
unknown是任意类型的父类型,仅此而已。
很多场景下,我们不好定义类型,需要根据实际情况做判断来决定类型,此时如果用any就失去了校验的能力,用unknown有any的效果,又保留了校验的能力。
const str1: unknown = '123'const str2: any = '123'let sex: booleansex = str1 // 报错,因为str类型没有确定,是unknown类型,所以不能赋值给boolean类型sex = str2 // 正常,因为any可以赋值给任意类型
枚举
可以给数值赋予名字,当我们使用的时候,就可以使用名字而不需要记数字,增加了代码的可读性。
// 定义一组角色,每个角色都用一个数字代表enum Roles { ADMIN = 0, USER = 1, MANAGER = 2}
类型继承
接口可以继承,提高了接口的可复用性
interface Animal { type: string;}interface Dog extends Animal{ name: sting; age: number;}interface TaiDi extends Dog { color: string;}
交叉类型
通过&,将多个类型聚合
很多时候,我们不会将类型写在一个type下面,而且拆分成很多个type,通过将这些不同的type聚合组合成一个新的type。想过与interface的extends很相似
type T1 = {name: string}type T2 = {age: number}type T3 = T1 & T2 // {name: string;age: number}
联合类型
通过|,使一个类型支持多个类型,听起来比较绕,我们直接看例子。
// value 支持string和number类型type value = string | number;// value 支持number和null类型type value = number | null// score只能支持1,2,3,4这四个数值type score = 1 | 2 | 3 | 4
对于联合类型,可以让某个变量的值可以支持多种类型,通过这种方式可以避免写any的情况。
交叉类型,简单理解就是合并两个对象类型,很像Object.assign。一个是对于类型,一个是对于对象。
断言
我们希望写的代码定义的某个变量是什么类型,之后使用就是什么类型。
可以是在多人协作中,以及组件参数的传递中,还有接口后来返回值的类型和一开始定义的不一样。
这个时候就可以使用断言,强制定义某个变量是什么类型。
可以理解成虽然某个变量定义的string类型,但是我知道在某个逻辑下会变成number类型,然后告诉编译器当作number类型吧,我拍胸脯保证不会错的。
// 例子1,常见就是从JAVAScript迁移代码到TypeScriptconst foo = {}foo.bar = 123 // Property 'bar' does not exist on type '{}'.(2339)interface Foo { bar: number}const foo = {} as Foofoo.bar = 123 // 正常赋值// 例子2const nums: number[] = [1,2,3,4,5,6] // 定义number数组// getNum类型是number | undefinedconst getNum = nums.find(item => item > 2) // 获取大于2的第一个元素// Type 'undefined' is not assignable to type 'number'.(2322)const num : number = getNum // 我们知道这段代码的getNum肯定能获取到值,所以修改如下可以正常const num: number = getNum as number
函数
// 基本使用function sum(x: number, y: number) : number { return x + y}// 可选参数,通过 ? 实现function sum(x: number, y: number,z?: number) : number { if(z) { return x + y + z } else { return x + y }}// 参数默认值function sum(x: number, y: number = 0) : number { return x + y}// 重载// TypeScript的函数重载通过为一个函数指定多个函数类型定义,从而对函数调用的返回值进行检查function sum(a: number,b:number):numberfunction sum(a: string,b:string): stringfunction sum(a: string | number,b: number | string) : string | number { if(typeof a === 'number' && typeof b === 'number') { // 如果是数值就相加 return a + b } else { // 否则字符串通过逗号拼接 return a + ',' + b }}// 大家可以试一下,如果不用重载的话,返回的类型就是string | number// 之后在使用ans1和ans2就还得需要断言const ans1 = sum(1,2) // ans返回的number类型const ans2 = sum('1','2') // ans返回的string类型
常用的内置方法使用
掌握以下方法让我们写TypeScript事半功倍,通过一些类型转换就可以得到自己想要的类型,而不需要重新定义。
PIck<T,K>
从T集合里面获取K的子级
type Type = { id : number; age: number; name: string;}// SubType的类型是{id:number; name: string;}type SubType = Pick<Type, 'id' | 'name'>;
keyof
获取一个类型的所有键值
type Person = { name:string; age:number}type Key = keyof person // 'age' | 'age'
Partial<T>
将所有属性变为可选。
我们定义某个对象的类型的时候,一开始大部分都会定义成必传的。之后,在另一个地方使用需要将其变成可选,这是便可以使用Partial定义一个新类型,而不改变之前的类型。
interface Todo { title: string; description: string;}// TodoPartial = {name?:string;age?:number}type TodoPartial = Partial<Todo> // description已经成为可选键值,所以不写也可以const todo: TodoPartial = {title: 'sanmu'}
Required
和Partial相反,将可选全部变为必填
interface Props { a?: number; b?: string;} const obj: Props = { a: 5 };// Property 'b' is missing in type '{ a: number; }' // but required in type 'Required<Props>'.// b没有传,报错const obj2: Required<Props> = { a: 5 };
Readonly<T>
将所有的键值对设置为只读
interface Todo { title: string;}const todo: Readonly<Todo> = { title: "Delete inactive users",};// Cannot assign to 'title' because it is a read-only property.// 因为title是只读,所以报错todo.title = "Hello";
Exclude<T,K>
从T集合中,剔除T类型和U类型的交集,返回剩余部分
type T = Exclude<'a' | 'b' | 'c', 'a'> // 'b' | 'c'
Omit<T,K>
从T集合中,过滤掉K的键值对
interface Todo { title: string; description: string; completed: boolean; createdAt: number;}type TodoInfo = Omit<Todo, "completed" | "createdAt">;// TodoInfo 只有title和descriptionconst todoInfo: TodoInfo = { title: "Pick up kids", description: "Kindergarten closes at 5pm",};
Record<T,K>
构造一个对象类型,T表示对象的属性值,K标识对象的属性值
interface CatInfo { age: number; breed: string;}type CatName = "miffy" | "boris" | "mordred";// cats的keys是 "miffy" | "boris" | "mordred",value是{ age,breed}const cats: Record<CatName, CatInfo> = { miffy: { age: 10, breed: "Persian" }, boris: { age: 5, breed: "Maine Coon" }, mordred: { age: 16, breed: "British Shorthair" },};
NonNullable
去除null和undefined。
开发中经常其他人定义了某个类型是包含null或者undefined,可是某些场景并不需要空值,这个时候不想重新写一个新类型,便可以使用这个
type T0 = NonNullable<string | number | undefined>;// T0 = string | numbertype T1 = NonNullable<string[] | null | undefined>;// T1 = string[]
Parameters<T>
获取函数的参数。
比如函数1的第一个参数的类型和函数2的第一个参数的类型是一样的,可以通过这个方法获取函数2的参数赋予给函数1的参数。
// T0 = [] 函数参数是空值type T0 = Parameters<() => string>;// T1 = [s: string]type T1 = Parameters<(s: string) => void>;declare function f3(arg: { a: number; b: string }): void;// T3 = [arg: {a: number;b: string;}]type T3 = Parameters<typeof f3>; // 将f4的第一个参数赋予f5函数的第1个参数declare function f4 (a: number, b: string ): void;// T4 = [a: number, b: string]type T4 = Parameters<typeof f4> // a: numberdeclare function f5(a: T4[0],b:string): void
ReturnType
Parameters获取的函数的参数类型,ReturnType则是获取函数的返回值的类型
// T0 = stringtype T0 = ReturnType<() => string>; // T1 = voidtype T1 = ReturnType<(s: string) => void>;// T2 = unknowntype T2 = ReturnType<<T>() => T>;declare function f1(): { a: number; b: string };// T4 = { a: number; b: string;}type T4 = ReturnType<typeof f1>;