几个迷惑的关键字
any 和 unknown 区别
主要区别是 unknown 类型会更加严格:在对 unknown 类型的值执行大多数操作之前,我们必须进行某种形式的检查。而在对 any 类型的值执行操作之前,我们不必进行任何检查。总之, any 和 unknown 都是顶级类型,但是 unknown 更加严格,不像 any 那样不做类型检查,反而 unknown 因为未知性质,不允许访问属性,不允许赋值给其他有明确类型的变量。
- 联合类型中的 unkown:
type UnionType1 = unknown | null; // unknown
type UnionType2 = unknown | undefined; // unknown
type UnionType3 = unknown | string; // unknown
type UnionType4 = unknown | number[]; // unknown
在联合类型中,unknown 类型会吸收任何类型。这就意味着如果任一组成类型是 unknown,联合类型也会相当于 unknown 。
type UnionType5 = unknown | any; // any
意外是 any 类型,如果至少一种组成类型是 any,联合类型会相当于 any 。
- 交叉类型中的 unkown :
type IntersectionType1 = unknown & null; // null
type IntersectionType2 = unknown & undefined; // undefined
type IntersectionType3 = unknown & string; // string
type IntersectionType4 = unknown & number[]; // number[]
type IntersectionType5 = unknown & any; // any
由于每种类型都可以赋值给 unknown 类型,所以在交叉类型中包含 unknown 不会改变结果。我们将只剩下 string 类型。
问题:如何将 unknown 类型指定为一个更具体的类型?
- 使用 typeof 进行类型判断(这些缩小类型范围的技术都有助于TS基于控制流程下的类型分析)
function unknownToString(value: unknown): string {
if (typeof value === "string") {
return value;
}
return String(value);
}
- 对 unknown 类型使用类型断言
要强制编译器信任类型为 unknown 的值为给定类型,则可以使用类型断言:
const value: unknown = "Hello World";
const foo: string = value; // Error
const bar: string = value as string; // OK
断言错了时语法能通过检测,但是运行的时候就会报错了!
const value: unknown = "Hello World";
const bar: number = value as number; // runtime Error
关于 any 和 unknown 的区别,看这篇文章。 any 和 unknown 傻傻分不清楚? - 掘金 (juejin.cn) 个人理解 unknown :它是所有类型的并集,如果使用一个 unknown 变量且被赋值,需要对该变量进行类型收窄操作。
never 和 void
在TS中,有一个很特殊的类型,就是 never,never 只能在两种情况下使用。
- 函数永远不会有返回值时,例如 while(true){}
const add: ()=>never = () => {
while(true){ i++ }
}
- 函数永远会抛出一个错误时。
const fn: ()=>never = () => {
throw new Error('error')
}
- 与void的差异 void 和 never 都是表示一个函数没有返回值,但是他们之间最大的区别是,void 表示可以被赋值的类型,never 表示其他任何类型也不能被赋值给它,它只能是 never 。
type 和 interface
两者区别:interface 只描述对象,type 则描述所有数据;type 只是别名,interface 则是类型声明;type 不可重新赋值,interface 会自动合并(可用于扩展某类型);对外暴露 api 尽量用 interface,方便扩展,对内 api 尽量用 type ,防止代码分散。
declare global {
interface String {
padZero(length: number): string;
}
}
infer 关键字
用于推断泛型参数,显示地声明泛型变量。
type getIntersection<T> = T extends (a: infer P,b: infer P) => void ? P : never;
type Intersection = getIntersection<(a: string, b: number)=> void> // string & number
- infer 必须在 extends 右侧使用,因为必须保证这个已知类型是由右侧的泛型推出来的,不然推导它的参数还有什么意义呢?检查时会跳过使用了 infer 的地方。
infer
可以在extends
的条件语句中推断待推断的类型。
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
type func = () => number;
type variable = string;
type funcReturnType = ReturnType<func>; // funcReturnType 类型为 number
type varReturnType = ReturnType<variable>; // varReturnType 类型为 any
infer
的作用不止是推断返回值,还可以解包。
type Ids = number[];
type Names = string[];
type Unpacked<T> = T extends (infer R)[] ? R : T;
type idType = Unpacked<Ids>; // idType 类型为 number
type nameType = Unpacked<Names>; // nameType 类型为string
type Response = Promise<number[]>;
type Unpacked<T> = T extends Promise<infer R> ? R : T;
type resType = Unpacked<Response>; // resType 类型为number[]
- 推断联合类型,同一个类型变量在推断的值有多种情况的时候会推断为联合类型,针对这个特性,很方便的可以将元组转为联合类型。
type ElementOf<T> = T extends (infer R)[] ? R : never;
type TTuple = [string, number];
type Union = ElementOf<TTuple>; // Union 类型为 string | number