TS中特殊类型-any、unknown、never和extends继承约束、keyof的使用
一、any
any类型是没有任何限制的,一旦变量设置为any等于是把类型检查关闭了,TS不会去进行校验,
个人认为既然使用了TS,尽可能还是不要使用any,除非是为了把js项目快速过渡到TS项目,把复杂的类型先用any定义,让项目能够快速启动,但是建议后续还是需要把any重写成对应的类型
二、unknown
unknown类型是TypeScript 3.0引入的,被称作安全的any。
unknown类型是安全的,虽然任何值都可以赋给unknown,
但是我们在使用unknown时如果没有进行类型断言或基于控制流的类型细化时,unknown不可以赋值给其它类型(除了unknown和any外)
同理,在unknown没有被断言或细化到一个确切类型之前,是不允许在其上进行任何操作的。
/* 可以把任何值赋值给unknown,但在使用时需要断言确定类型:as、typeof 等等 */ let anyName: any = "我是任何呀"; let unknownName: unknown = "我不知道呀"; let myName: string; myName = anyName; // any赋值给其他类型,可以正常编译 // myName = unknownName; // unknown在没有断言前,赋值给其他类型,编译报错 myName = unknownName as string; // unknown在这里断言为string,可以赋值给string,正常编译 let unknownNum: unknown; unknownNum = "123"; // 没有使用前,定义为string,正常编译 unknownNum = 456; // 没有使用前,定义为number,正常编译 let myNum: number; // myNum = unknownNum + 20; // 编译报错,因为unknown没有断言,TS不知道这是什么类型 myNum = (unknownNum as number) + 20; // 编译正常,unknown断言为number,可以进行加法 // 即使是明确定义了一个对象,但是类型为unknown时,在没有断言前还是不能使用对象的方法或属性 let obj: unknown = { test: '测试属性' }; // console.log(obj.test); // 报错,因为unknown没有断言,TS不知道这是什么类型,不允许操作 // 定义一个类型 interface MyObject { test: string; } function printUnknown(unknownObj) { unknownObj as MyObject console.log(unknownObj.test); } printUnknown(obj);
三、never
在TypeScript中,never类型表示那些永不存在的值的类型。它通常用于表示不可到达的代码分支或抛出异常的函数。
never类型表示那些永远不会发生的类型
例如
当一个函数总是抛出异常或进入无限循环: while(true) {}
或者总是会抛出异常: function foo() { throw new Error('Not Implemented') }
返回类型就是never.
never类型的应用场景:
- 主要用来进行编译时的全面的检查,例如你函数里面的 if else if else 分支,是否已经穷尽了所有可能
- 进行类型校验
/* 只有never类型本身可以赋值给never类型 */ type reqType = "get" | "post"; function req(method: reqType) { if (method === "get") { console.log("GET 请求"); } else if (method === "post") { console.log("POST 请求"); } else { // 这里never代表我们已经把所有分支情况都处理了,如果reqType还有一个类型是 'put' 那么这就会报错 const rejectMethod: never = method; } }
/* 校验参数类型 */ function countNum<T>(n: T extends number ? T : never) { return n; } // 这里可以判断入参是否为number,如果不是,那么T就是never,赋值给never就会报错 countNum(1); // 编译成功 countNum("a"); // 编译报错
四、extends
关键字既可以用作类继承,也可以用作泛型约束。
类继承中,extends
用于表示类之间的继承关系:
class Animal { jiao(hour: number) { console.log(`Animal叫了这么久${hour}h.`); } } class Dog extends Animal { fei() { console.log('wangwang!'); } }
泛型约束中,extends
用于为泛型变量指定类型约束:
function howLong<T extends { length: number }>(arg: T) { console.log(arg.length); } howLong<string>('hello'); // 5 /* 这里T被约束为具有length属性的类型,这意味着传给howLong的参数arg必须有一个length属性,且其类型为数字。 这里extends更像是一个判断条件,用于确保泛型参数类型符合特定的约束。*/
五、keyof
keyof T
是 T
类型的键集
/* keyof T 是 T 类型的键集 */ interface Home { addr: string; height: string; } type h = keyof Home; // 这里 h 就等于 "addr" | "height" /* 使用 keyof 进行映射类型,需要注意的是映射类型只能在类型别名(type)中使用,不能在接口中使用 */ interface K { a: number; b: number; } type V1 = { a: number; b: number } // 上面的写法可以用keyof简化 type V2 = { [key in keyof K]: number }