typescript类型推导
最近与群友讨论type运算相关的问题,发现了以前对typescript的学习并不健全,很多的特性中文文档上并不存在,在我的知识认知中也没有什么了解,于是我怒翻官方原文档,发现了相关的英文介绍https://www.typescriptlang.org/docs/handbook/2/types-from-types.html,并以本文做一个粗略的使用总结。
本文特别感谢LydiaYuan小姐姐提供的问题
# keyof & typeof & Indexed Access Types
keyof 返回类型包含整个对象的所有key,并以|连接所有key
typeof 当返回类型需求为type时,返回变量的所属类型,为变量时按照js的方式返回字符串,具体返回对照表
Indexed Access 参考下面示例
type Person = { age: number; name: string; alive: boolean };
type Age = Person["age"];
// ^ = type Age = number
type I1 = Person["age" | "name"];
// ^ = type I1 = string | number
# 类型模板字符串
type World = "world";
type Greeting = `hello ${World}`;
// ^ = type Greeting = "hello world"
type A = true | 1 | 'aaa';
type D = `${A}`;
// ^ = type D = "true" | "1" | "aaa"
# 类型推导
type 属性支持类型的推导计算,使用示例
type isString<T extends any> = T extends string ? true : false
type A = isString<number>
// false
type B = isString<'aaa'>
// true
推导过程中我们还可以通过使用infer获取数组中的元素
type Flatten<Type> = Type extends Array<infer Item> ? Item : Type;
# 类型推导上的一些思考
# 什么是extends判断
当我们判断A extends B时,A是B或者B的子集,按照表单式type C<A extends any> = A extends B ? true : false
来看,A若是B或是B的子集(继承于B类型),则C为true
# 我们遇到的问题
在ts类型推导过程中,我们发现any会产生一些奇怪的影响,有时候就需要筛选出来做特殊处理,但是我们反复翻阅文档,并没有一个官方提供的可行方案,但是我们知道这样的特殊类型有3个 any\unknow\never
群里聊天时小姐姐写了这样一个表单式
type Filter<T extends any[], A> = T extends [infer F, ...(infer R)] ? F extends A ? [F, ...Filter<R, A>] : Filter<R, A> : []; type A = Filter<[1, "BFE", 2, true, "dev"], number>; // [1, 2] OK type B = Filter<[1, "BFE", 2, true, "dev"], string>; // ['BFE', 'dev'] OK type C = Filter<[1, "BFE", 2, any, "dev"], string>; // ['BFE', 'dev'] | ['BFE', any, 'dev'] Not OK // should be ['BFE', any, 'dev']
在这个示例中我们可以发现
any extends string
返回了true | false
,与后续类型结合产生了或连接的类型。
由上面引用的示例,我去测试了一下any\unknow\never之间的关系,和对推导式结果的影响。
never的影响比较奇怪我们还在实验,似乎会让任何和其有些关联的类型都变成never。
但是关于any,我做了一些思考。
测试中我发现any extends 标准类型 时候都会是 true | false,从中我认为,官方对any的设定是一个可以而又不可以的存在,而unknow是一个未知。
于是,做了一个大胆的设想,就是任何类型都可以extends any也可以extends unknow,那么如果我让泛型和unknow反过来,也就是 unknow extends T,这时候 T 除了any、unknow都应该是false,于是成功了。
type Filter<T extends any[], A> = T extends [infer F, ...(infer R)]
? unknown extends F
? [F, ...Filter<R, A>]
: F extends A
? [F, ...Filter<R, A>]
: Filter<R, A>
: [];
type A = Filter<[1, "BFE", 2, true, "dev"], number>;
// [1, 2] OK
type B = Filter<[1, "BFE", 2, true, "dev"], string>;
// ['BFE', 'dev'] OK
type C = Filter<[1, "BFE", 2, any, "dev"], string>;
// ['BFE', any, 'dev'] OK