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 TT 类型的键集

/* 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 }

 

posted @ 2024-08-20 23:04  我用python写Bug  阅读(336)  评论(0编辑  收藏  举报