TypeScript类型编写提升

  |  

容易忽略的关键字

typeof 关键字

Typescript 可以使用typeof关键字作为类型保护, 同样的和js一样还存在 instanceof、 in 等关键字

ts中通过typeof 类可以获得类的类类型,直接使用类作为类型此时使用的是类的实例类型。

更多的信息: 查看官方文档https://www.typescriptlang.org/docs/handbook/2/typeof-types.html

const school = "school"
let n: typeof school
// 等于 let n:string

// 与 ReturnType 内置类型结合使用

function f() {
return { x: 10, y: 3 }
}
type P = ReturnType<typeof f>

// type P = {
// x: number;
// y: number;
// }

keyof 关键字

keyof操作符会将一个对象类型(注意这里是类型并不是值)的key组成联合类型返回。

有点 Object.keys(obj) 的意思,只不过要关注的是,这里是取出所有的key做联合(a | b | c)

type ArrayIsh = { [n: number]: unknown };
type A = keyof ArrayIsh;

// type A = number

type MapIsh = { [k: string]: boolean };
type M = keyof MapIsh;

// type M = string | number

而 keyof 与 in 关键字组合起来,就可以复制一个类型(额,只是为了演示,实际开发大可不必)

interface Test {
t: string
k: number
}
type Copy<T> = {
[K in keyof T]: T[K]
}

type CopyTest = Copy<Test>

const fff: CopyTest = {
t: "1",
k: 1
}

extends 关键字

Ts中extends除了用在继承上,还可以表达泛型约束,通过extends关键字可以约束泛型具有某些属性。

function loggingIdentity<T>(arg: T): T {
console.log(arg.length) // Ts语法错误 T可以是任意类型,并不存在length属性
return arg
}

// 采用下面的方式
interface Lengthwise {
length: number
}

// 表示传入的泛型T接受Lengthwise的约束
// T必须实现Lengthwise 换句话说 Lengthwise这个类型是完全可以赋给T
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length) // OK
return arg
}

// 受泛型约束
loggingIdentity(3); // Error
loggingIdentity({length: 10, value: 3}) // OK

// 🌰 获取 obj[key]

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key]
}

const x = {a: "zz", b: 2, c: 3, d: {test: 1}}

getProperty(x, "a") // okay
getProperty(x, "e") // error

infer(推断的意思) 关键字

表示在 extends 条件语句中 待推断的类型变量, 必须联合 extends 出现

注意⚠️: infer 跟随 extends 成对出现, infer 表示待推断

使用

type ParamType<T> = T extends (...args: infer P) => any ? P : T;

interface User {
name: string;
age: number;
}

type Func = (user: User) => void;

type Param = ParamType<Func>; // Param = User
type AA = ParamType<string>; // string

内置类型

partial 部分的, 局部的

作用: 将对象的第一层属性转为可选的

如果有很多属性树非必需的,那么可以采用这个一次性全部转了

根据这个描述,其实partial的原理非常的简单

type Partial<OBJ> = {
[key in keyof OBJ]?: OBJ[key]
}
interface Coord = {
x: number,
y: number,
obj: {
t: string
}
}

type Coord2 = partial<Coord>

// 等同于

interface Coord3 {
x?: number | undefined,
y?: number | undefined,
obj?: { t: string} | undefined
}

// 如果想递归的将属性转为可选, 那么可以这样
interface Person {
name: string
age: number
detail: {
school: string
company: string
}
}
type DeepPartial<T> = {
// 判断 T[K] 是否是对象, 是的话递归
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K]
}
type DPerson = DeepPartial<Person>

Required

Required全部的意思, 在ts 中表示将 全部的属性转为必选的。 与Partial 意思刚好相反,同样不支持深层属性,需要递归。

type Req<T> = {
[K in keyof T]-?: T[K]
}

-? 什么东西?

没错, 正是通过在属性后面采用 -? 定义属性为必填的。

对于深层的属性,同样的采用递归的方式来实现

type DeepRequired<T> = {
[K in keyof T]-?: T[K] extends object ? DeepRequired<T[K]> : T[K];
}

例子不举了

Exclude<T,U>

排除 T 类型中满足 U 的类型从而返回新的类型。 主要是针对于 联合类型(string|number|array)来操作。

简单说就是: 排除 T 中包含的 U 部分, 返回 剩下没排除的。

let a: string | number; // 需要采用 let  因为 type a 的话, 下面的 typeof a 就识别不了

// 排除 typeof a => [string, number] 中的 string , 因此剩下 number
type CustomType = Exclude<typeof a, string>; // number 类型

原理实现: 泛型 配合 extend 已经条件运算符

type Exclude<T, U> = T extends U ? never : T

// never 代表的也就是一个无法达到的类型,不会产生任何的效果,自然就会被忽略

Extract<T,U>

extract 的含义与 exclude 相反, 他会挑出 T 中符合 U 的类型, 而非排除

let a: string | number; 
type CC = Extract<typeof a, number | Array<number>>
const c: CC = [12]

Pick<Type,Keys> 关键字

pick: 挑选的意思

从 type中挑选出 keys, 从而返回新的类型

interface Props {
name: string,
label: number,
value: boolean
}
type ChildrenProps = Pick<Props, 'name' | 'label'> // {name: string, label: number}

原理实现

type Pick<T, K extends keyof T> = {
[P in K]: T[P]
}

Parameters<T,>

Parameters<T,> 用于获取函数的参数类型组成的元组类型[a:string, b:number]

const fn = (a:string, b: number, ...c: number[]) => {}

type c = Parameter<typeof fn>
// c [a:string, b: number, ...c:number[]]

源码实现:

type Parameters<T extends (...args:any) => any> = T extends (args: infer P) => any ? P : never

Omit<T,K> 关键字

omit: 忽略的意思

从另一个对象类型中剔除某些属性,并创建一个新的对象类型。

type User = {
id: string,
name: string,
email: string
}

type UserWithoutEmail = Omit<User, 'email'>

// 剔除了 email 这个属性, 因此等价于下面的

type UserWithoutEmail = {
id: string,
name: string
}

ReturnType<T,>

接受传入一个函数类型为泛型,返回值为函数的返回类型

type T0 = ReturnType<() => string>; // type T0 = string

type T1 = ReturnType<(s: string) => void>; // type T1 = void

Record<Keys,type> 关键字

构造一个新的对象类型,其属性键为Keys(联合类型),属性值为Type。

type keys = 'name' | 'title' | 'hello'

interface value {
name: string,
label?: number
}

// Record 内置类型,可以将传入的 keys 联合类型遍历作为 key
// 为每一个key 的value赋值 为 values 从而形成一个全新的对象类型返回

// 遍历 keys => name | title | hello , 生成 name = value, title = value, hello = value

// 而 value = {name: string, label?: number}
const b: Record<keys, value> = {
name: {
name: 'ww',
label: 1
},
title: {
name: 'ls',
label: 2
},
hello: {
name: 'zs'
}
}


// 而 Record 常用于 遍历对象返回新的类型时使用
function mapping<K extends string | number | symbol, V, R>(
obj: Record<K, V>,
callback: (key: K, value: V) => R
): Record<K, R> {
const result = {} as Record<K, R>
Object.keys(obj).forEach(key => {
const parseKey = key as K
const value = obj[parseKey]
result[parseKey] = callback(parseKey, value)
})
return result
}

mapping({name: "19", company: "Tc"}, (key, value) => {
return key + value
})

×

纯属好玩

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

文章目录
  1. 1. 容易忽略的关键字
    1. 1.1. typeof 关键字
    2. 1.2. keyof 关键字
    3. 1.3. extends 关键字
    4. 1.4. infer(推断的意思) 关键字
  2. 2. 内置类型
    1. 2.1. partial 部分的, 局部的
    2. 2.2. Required
    3. 2.3. Exclude<T,U>
    4. 2.4. Extract<T,U>
    5. 2.5. Pick<Type,Keys> 关键字
    6. 2.6. Parameters<T,>
    7. 2.7. Omit<T,K> 关键字
    8. 2.8. ReturnType<T,>
    9. 2.9. Record<Keys,type> 关键字
,