EXIT

00:00:00

TS类型体操

keyof any是什么类型?

type AnyKeys = keyof any; // "string" | "number" | "symbol"

由于 any 可以表示任何属性,理论上 keyof any 应该包含所有可能的字符串索引。然而,实际上 TypeScript 的类型系统并不支持无限的类型,因此 keyof any 的结果被特殊处理为 string | number | symbol

同理:unknow

type UnknownKeys = keyof unknown; // "string" | "number" | "symbol"

为什么有时用 unknow 代替 any?

未知类型代表任何值。它与 any 类型类似,但更安全,因为对未知值做任何操作都是不合法的. unknow类型的出现就是为了解决any类型污染其他变量的问题.

//any污染问题 let x:any = 'hello'; let y:number; y = x; // 不报错 y * 123 // 不报错 y.toFixed() // 不报错

unknow解决类型污染问题:

  • unknown类型的变量,不能直接赋值给其他类型的变量,除了unknow和any类型,因为他俩代表所有类型.

    这就避免了污染问题,从而克服了any类型的一大缺点。

    let v:unknown = 123; let v1:boolean = v; // 报错
  • 其次,不能直接调用unknown类型变量的方法和属性。因为我们不知道unknow类型有什么属性和方法.

    let v1:unknown = { foo: 123 }; v1.foo // 报错 let v2:unknown = 'hello'; v2.trim() // 报错 let v3:unknown = (n = 0) => n + 1; v3() // 报错

那么什么时候使用unknow类型呢?

  • 当一个类型不确定时使用.或者需要类型缩小时使用.
    let a:unknown = 1; if (typeof a === 'number') { let r = a + 10; // 正确 }

有了Symbol类型为什么设计师还设计出了unique symbol类型?

因为Symbol类型包含了所有的Symbol值,但是无法表示某一个具体的Symbol值.但是我们在使用Symbol类型时,都是有且仅使用一个具体的值.这就好比:

Symbol类型相当于string unique symbol类型相当于'a' 我们在创建使用symbol类型时,只会使用类型'a'

Interface 和 type 的不同之处

  • interface不能包含属性映射(mapping).type可以.

    interface Point { x: number; y: number; } // 正确 type PointCopy1 = { [Key in keyof Point]: Point[Key]; }; // 报错 interface PointCopy2 { [Key in keyof Point]: Point[Key]; };
  • ...

函数重载

在进行普通函数重载时,例如:

function fn1... function fn1... function fn1... let t = fn1(...) //从最先声明的函数开始匹配

如果是interface接口合并.(声明多个interface可以将多个interface合并成一个,该interface包括之前interface声明的所有属性(彼此不能有类型冲突),如果是函数,将触发函数重载.)

interface Cloner { clone(animal: Animal): Animal; } interface Cloner { clone(animal: Sheep): Sheep; } interface Cloner { clone(animal: Dog): Dog; clone(animal: Cat): Cat; } // 等同于,并且,越靠后的定义,优先级越高,排在函数重载的越前面。 interface Cloner { clone(animal: Dog): Dog; clone(animal: Cat): Cat; clone(animal: Sheep): Sheep; clone(animal: Animal): Animal; }

TS中的Infer是什么意思?

infer关键字用来定义泛型里面推断出来的类型参数,而不是外部传入的类型参数。

它通常跟条件运算符一起使用,用在extends关键字后面的父类型之中。

键名重映射!!

type A = { foo: number; bar: number; }; type B = { [p in keyof A as `${p}ID`]: number; }; // 等同于 type B = { fooID: number; barID: number; };

TS中可以用模板字符串!

可以根据键名重映射做许多操作.例如:

//属性过滤 type User = { name: string, age: number } type Filter<T> = { [K in keyof T as T[K] extends string ? K : never]: string } type FilteredUser = Filter<User> // { name: string }

TS中的extends关键字用法

用法1: 用来继承. 例如接口和类.

用法2: 用来条件判断.

​ 例如: A extends B:

如果类型A能够赋值给类型B,那么表达式判断为真,否则为假

​ 1. 也就是类型A东西要多,类型B东西要少,类型A才能赋值给类型B(类型B = 类型A -> 类型B所需要的东西 类型A一定要有,多了无所谓.).那么表达式为真,否则为假.

​ 2. 也就是A是B的子类,则表达式为真,否则为假.

​ 3. 就像用法1一样:A extends B 表示A是B的子类,A肯定要比B的东西多.

​ 以上3条适用于对象类型的.对于一般类型,例如:true extends boolean 相当于: ​ true extends true|false,这样是对的.

索引签名

索引签名只能用[string,number,symbol],使用其他类型会报错.(也不能使用类型作为索引).

  • 对于boolean类型,使用索引签名会导致:

    type A = [boolean]; type C = { [P in `${A[number]}`]: `${P}`; } //C = {true:true,false:false}
  • 对于联合类型,使用索引签名会导致:

    type A = 1|2|3 type B = { [p in `${A}`]:true } //B = {1:true,2:true,3:true}

TS中的PromiseLike类型

就是Promise类型.

TS中的泛型

泛型可以在对象/函数中使用:

先定义类型:

//对象 interface ObjType<T> { arg1:T, arg2:T } const obj:ObjType<number> = { arg1:1, arg2:1 } //函数 type FnType<T> = (arg:T) => T const fetch:FnType<number> = (arg)=>{ return arg }

直接在函数中定义:(对象中不能直接定义)

const fetchFn:<T>(arg:T)=>T = (arg) => { return arg; } fetchFn(1);

*Equal是怎么实现的?

Equal是用来判断两个类型是否严格相等.

具体解释:https://stackoverflow.com/questions/68961864/how-does-the-equals-work-in-typescript/68963796#68963796

函数的keyof and extends

对于一个函数类型为:

type FN = () => 22;

其实其可以转换为一个对象:(但是其实是没有这个属性的,可以将这个属性看作 never 类型)

type Fn = { 'someCallName':() => 22; }

因此,keyof Fn = never;

但是:

type ex = FN extends Record<never,any> ? true : false 结果为true type ex = FN extends Record<string,any> ? true : false 结果为true type ex = FN extends Record<number,any> ? true : false 结果为false

很奇怪...

函数的泛型

函数的泛型一般如此表示:

type FnType<T,K> = (key:T,value:K) => void; //或者 function handleFnJS<T,K>(key:T,value:K) { //一些逻辑 }

还有一种函数的表示就是在接口或者对象里面,例如:

interface outType { handle<K,T>(key:K,val:T):string; //或者是 handle:<K,T>(key:K,val:T) => string; }

数组的keyof and extends

对于一个数组[1,2,3..]来说,其本质上是一个对象:

{ 0:1, 1:2, 2:3, pop:()=>...., push:()=>..., `symbol`:xxx }

因此其keyof [1,2,3..] 应该是 string | number | symbol,而对应的值更是五花八门了.

那么为什么:

type Arr = [1, 2, 3, 4]; type ex = Arr extends Record<string,any> ? true : false; 结果会是true?

extends判断的是前者是否为后者的子类,很明显,keyof Arr = string | number | symbol.

如果直接keyof Arr extends string,则肯定为false.

但是对于对象来说,子类拥有的属性之类的要比父类多才符合 extends.

declare 关键字

使用方法:

declare const te:TypeTe;

这一段表明te这个变量已经被声明,并且其类型为TypeTe.declare表明该变量一定存在并且一定遵循TypeTe这样一个类型.

类型中如何扩展对象

例如,当你定义了一个对象类型时:

type sc = { [k in string] : number; name:number; }

这时会进行报错:映射的类型可能不声明属性或方法

这时可以通过:

  • type定义的可以通过 & {name:number}来进行拓展.
  • interface 定义的可以通过extends来进行扩展.
uid:9nhYrS
VOIDIS.ME
  1. no-like
  2. message
  3. Bilibili
  4. Github
  5. RSS
  6. sun