泛型也是一种类型,只不过不同于 string, number 等具体的类型,它是一种抽象的类型,我们不能直接定义一个变量类型为泛型。
TS 提供了 inteface 和 type 关键字供我们定义自己的类型,之后就能像使用基本类型一样使用自己定义的类型了。
提供了各种逻辑运算符,比如 &, | 等 ,供我们对类型进行操作,从而生成新的类型。
提供泛型,允许我们在定义的时候不具体指定类型,而是泛泛地说一种类型,并在函数调用的时候再指定具体的参数类型。
值的集合就是类型,平时写代码基本都是对值编程,TS 提供了很多类型(也可以自定义)以及很多类型操作帮助我们限定值以及对值的操作。
1 if (person.isVIP) { 2 console.log('VIP') 3 } 4 if (cnt > 5) { 5 // do something 6 } 7 8 const personNames = persons.map(p => p.name) 9 ...
1 function t(name: string) { 2 return `hello, ${name}`; 3 } 4 t("lucifer");
泛型的写法就是在标志符后面添加尖括号(<>),然后在尖括号里写形参,并在 body(函数体, 接口体或类体) 里用这些形参做一些逻辑处理。

定义:

使用:

将类型看成值,然后对类型进行编程,这就是泛型的基本思想。泛型类似我们平时使用的函数,只不过其是作用在类型上,思想上和我们平时使用的函数并没有什么太多不同,泛型产生的具体类型也支持类型的操作。

思想上可以这样去理解。但是具体的实现过程会有一些细微差别,比如:
1 type P = [number, string, boolean]; 2 type Q = Date; 3 4 type R = [Q, ...P]; // A rest element type must be an array type.
1 type Lucifer = LeetCode; 2 type LeetCode<T = {}> = { 3 name: T; 4 }; 5 6 const a: LeetCode<string>; //ok 7 const a: Lucifer<string>; // Type 'Lucifer' is not generic.
做出改变:(ok)
1 type Lucifer<T> = LeetCode<T>;
T 理论上是可以是任何类型的,不同于 any,你不管使用它的什么属性或者方法都会报错(除非这个属性和方法是所有集合共有的)。那么直观的想法是限定传给 trace 函数的参数类型应该有 size 类型,这样就不会报错了。如何去表达这个类型约束的点呢?实现这个需求的关键在于使用类型约束。 使用 extends 关键字可以做到这一点。简单来说就是你定义一个类型,然后让 T 实现这个接口即可。
如果直接将T替换成Sizeable会有类型丢失的风险.可以继承多个interface
1 interface Sizeable { 2 size: number; 3 } 4 function trace<T extends Sizeable>(arg: T): T { 5 console.log(arg.size); 6 return arg; 7 }
React.FC 类型定义:
1 type FC<P = {}> = FunctionComponent<P>; 2 3 interface FunctionComponent<P = {}> { 4 (props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null; 5 propTypes?: WeakValidationMap<P>; 6 contextTypes?: ValidationMap<any>; 7 defaultProps?: Partial<P>; 8 displayName?: string; 9 }
将泛型类比到函数进行理解:
- 首先定义了一个泛型类型 FC,这个 FC 就是我们平时用的 React.FC。它是通过另外一个泛型 FunctionComponent 产生的。
- FunctionComponent 实际上是就是一个接口泛型,它定义了五个属性,其中后四个是可选的,并且是静态类属性。
- displayName 约束为string类型,而 propTypes,contextTypes,defaultProps 又是通过其他泛型生成的类型。
(props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null;是 FunctionComponent 的一个函数,接受两个参数(props 和 context )返回 ReactElement 或者 null。PropsWithChildren实际上就是往 props 中插入 children.
type PropsWithChildren<P> = P & { children?: ReactNode };
emmmm......突然发现 上面的看的一知半解 下面的代码能看懂:(ノω<。)ノ))。。。
1 type A<T = string> = Array<T>; 2 const aa: A = [1]; // type 'number' is not assignable to type 'string'. 3 const bb: A = ["1"]; // ok 4 const cc: A<number> = [1]; // ok
泛型支持函数嵌套
type CutTail<Tuple extends any[]> = Reverse<CutHead<Reverse<Tuple>>>;
Reverse 是将参数列表反转,CutHead 是将数组第一项切掉。因此 CutTail 的意思就是将传递进来的参数列表反转,切掉第一个参数,然后反转回来。
功能是切掉参数列表的最后一项。 eg:
function fn (a: string, b: number, c: boolean):boolean {}
那么经过操作
type cutTailFn = CutTail<typeof fn>
可以返回
(a: string, b:number) => boolean
具体实现可以参考:Typescript 复杂泛型实践:如何切掉函数参数表的最后一个参数?\( ̄︶ ̄*\)) 反正我现在也看不懂23333.....
递归调用,这个递归调用的功能是:递归地将类型中所有的属性都变成可选。类似于深拷贝那样,只不过这不是拷贝操作,而是变成可选,并且是作用在类型,而不是值。
type DeepPartial<T> = T extends Function ? T : T extends object ? { [P in keyof T]?: DeepPartial<T[P]> } : T; type PartialedWindow = DeepPartial<Window>; // 现在window 上所有属性都变成了可选啦
...( _ _)ノ|上面这句代码应该可以理解成:定义一个DeepPartial类型,该类型使用<T>中的T来进行约束,=后是关于T的一些操作
Partial
功能是将类型的属性变成可选。注意这是浅 Partial,DeepPartial 上面我讲过了,只要配合递归调用使用即可。
type Partial<T> = { [P in keyof T]?: T[P] };
Required
功能和Partial 相反,是将类型的属性变成必填, 这里的 - 指的是去除。 -? 意思就是去除可选,也就是必填。emmm,单个?是什么意思..
type Required<T> = { [P in keyof T]-?: T[P] };
TS太难啦つ﹏⊂
浙公网安备 33010602011771号