记录自己想到的和遇到的收集一些高级 TS 类型的代码

数组不为空

一道面试题,如何定义一个不是空数组的数组?

// 怎么定义类型,约束数组中值至少有一项
const a: number[] = []
点击查看答案
// 解法一
type NotAmptyArray<T> = [T, ...T[]]

// 解法二
type NotAmptyArray<T> = T[] & { 0: T }

全部可选的参数对象至少包含一个 key

自己想出来的一个问题,语言不好描述,看下面代码

interface A {
    a?: number
    b?: boolean
}

declare function main(opt: A): void

// 怎么定义一个类型,使得参数对象中至少包含一个 key?
main({})
点击查看答案
interface A {
    a: number
    b: boolean
}

type AtLeastOne<T> = {
    [K in keyof T]: Pick<T, K> & Partial<Omit<T, P>>
}[keyof T]

declare function main(opt: AtLeastOne<A>): void

main({ a: 1 })

给定的类型可选

在下面的实例中,实现 SetOptional,可使得给定的 key 为可选

type Foo = {
  a: number;
  b?: string;
  c: boolean;
}

// 实现 SetOptional
type SomeOptional = SetOptional<Foo, 'a' | 'b'>;

// type SomeOptional = {
//  a?: number; // 该属性已变成可选的
//  b?: string; // 保持不变
//  c: boolean; 
// }
点击查看答案
// 解法一
type SetOptional<T, N extends keyof T> = {
  [P in keyof T]: Partial<Pick<T, N>> & Pick<T, Exclude<keyof T, N>>
}[keyof T]

// 解法二
type Simplify<T> = {
  [P in keyof T]: T[P]
}
type SetOptional<T, K extends keyof T> =
 Simplify<Partial<Pick<T, K>> & Pick<T, Exclude<keyof T, K>>>

条件 Pick

根据值类型进行筛选

interface Example {
 a: string;
 b: string | number;
 c: () => void;
 d: {};
}

// 测试用例:
type StringKeysOnly = ConditionalPick<Example, string>;
//=> {a: string}
点击查看答案
// 首先将类型对应的 key 找出来,然后再进行 Pick
type ConditionalKeys<T, Condition> = {
  [P in keyof T]: T[P] extends Condition ? P : never
}[keyof T]

type ConditionalPick<T, Condition> = Pick<T, ConditionalKeys<T, Condition>>

函数插入参数

为已有的函数类型增加指定类型的参数,新增的参数名是 x,将作为新函数类型的第一个参数

type Fn = (a: number, b: string) => number
type AppendArgument<F, A> = // 你的实现代码

type FinalFn = AppendArgument<Fn, boolean> 
// (x: boolean, a: number, b: string) => number
点击查看答案
type Fn = (a: number, b: string) => number

// TS 中内置了 Parameters, 和 ReturnType 可以便捷的获取函数的参数和响应类型
type AppendArgument<F extends ((...args: any) => any), A> = (x: A, ...args: Parameters<F>) => ReturnType<F>

type FinalFn = AppendArgument<Fn, boolean>

扁平数组

type NaiveFlat<T extends any[]> = // 你的实现代码

// 测试用例:
type NaiveResult = NaiveFlat<[['a'], ['b', 'c'], ['d']]>
// NaiveResult的结果: "a" | "b" | "c" | "d"
点击查看答案
type NaiveFlat<T extends any[]> = {
  [P in keyof T]: T[P] extends any[] ? NaiveFlat<T[P]>: T[P]
}[number]

强制对象范围

type SomeType =  {
  prop: string
}

// 更改以下函数的类型定义,让它的参数只允许严格SomeType类型的值
function takeSomeTypeOnly(x: SomeType) { return x }

// 测试用例:
const x = { prop: 'a' };
takeSomeTypeOnly(x) // 可以正常调用

const y = { prop: 'a', addditionalProp: 'x' };
takeSomeTypeOnly(y) // 将出现编译错误
点击查看答案
type Exclusive<T1, T2 extends T1> = {
  [P in keyof T2]: P extends keyof T1 ? T2[P] : never
}

function takeSomeTypeOnly<T extends SomeType>(x: Exclusive<SomeType, T>) { return x }