主题
TypeScript
interface 和 type 区别
| 特性 | type | interface |
|---|---|---|
| ✅ 定义对象类型 | ✅ 可以 | ✅ 可以 |
| ✅ 定义基本类型别名 | ✅ 可以(比如 string, number[]) | ❌ 不可以 |
| ✅ 联合类型 / 交叉类型 | ✅ 可以 | ❌ 不支持联合 |
| ✅ 扩展能力 | ✅ 使用 & 交叉 | ✅ 使用 extends |
| ✅ 支持声明合并 | ❌ 不支持 | ✅ 支持 |
| ✅ 能表示函数、元组、联合、映射类型 | ✅ 可以 | ⚠️ 受限 |
| 推荐使用场景 | 更灵活的复杂类型 | 面向对象/组件设计中的结构扩展 |
一、 声明合并
ts
interface A {
x: number;
}
interface A {
y: number;
}
// 合并为:{ x: number; y: number }
// type 是不支持keyof T
表示类型
T的所有键的联合类型
ts
type T = { name: string; age: number };
type Keys = keyof T; // "name" | "age"
// K extends keyof T
// 这就表示:`K` 必须是 `"name"` 或 `"age"` 之一。Pick<T, K>
从一个类型对象中拣取想要的几个类型
- K必须是在T中的类型
- 遍历联合类型获得key值,然后元素在T对象中获得该元素
ts
type MyPick<T, K extends keyof T> = {
[P in K]: T[P];
};
type Person = { name: string; age: number; email: string };
type Picked = MyPick<Person, 'name' | 'email'>;
// -> { name: string; email: string }
// `Record<K, T>`(构造一个对象,key 是 K,value 是 T)
type UserRoles = 'admin' | 'user';
type RoleMap = Record<UserRoles, boolean>;
// -> { admin: boolean; user: boolean }Readonly< T >
js
type MyReadonly<T> = {
// 创建一个复制类型并且把所有属性设置为只读
readonly [t in keyof T]: T[t];
}
/* _____________ 测试用例 _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<MyReadonly<Todo1>, Readonly<Todo1>>>,
]
interface Todo1 {
title: string
description: string
completed: boolean
meta: {
author: string
}
}
// 将 T 中的所有属性变成“**可选且可修改**”的版本
type Crazy<T> = {
-readonly [P in keyof T]?: T[P]
}元组转化对象
js
/* _____________ 你的代码 _____________ */
/**
* 传入的值分别有string、number、symbol元素类型的数组
* 由于比对的是key、value是一致的,所以遍历数组的每一个元素作为key和value
*/
type TupleToObject<T extends readonly (string | number | symbol)[]> = {
[P in T[number]]:P
}
/* _____________ 测试用例 _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const
const tupleNumber = [1, 2, 3, 4] as const
const sym1 = Symbol(1)
const sym2 = Symbol(2)
const tupleSymbol = [sym1, sym2] as const
const tupleMix = [1, '2', 3, '4', sym1] as const
type cases = [
Expect<Equal<TupleToObject<typeof tuple>, { 'tesla': 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y' }>>,
Expect<Equal<TupleToObject<typeof tupleNumber>, { 1: 1, 2: 2, 3: 3, 4: 4 }>>,
Expect<Equal<TupleToObject<typeof tupleSymbol>, { [sym1]: typeof sym1, [sym2]: typeof sym2 }>>,
Expect<Equal<TupleToObject<typeof tupleMix>, { 1: 1, '2': '2', 3: 3, '4': '4', [sym1]: typeof sym1 }>>,
]
// @ts-expect-error
type error = TupleToObject<[[1, 2], {}]>never
表示没有返回值或无效匹配
infer
“类型推断工具”,只能用在
extends的条件类型中,来从类型结构中提取类型变量。
js
[infer A, ...infer Rest]
//等价于js
const arr = [1,2,3]
const [first,...reset] = arr; // 获得第一个元素和剩余元素,并且数组不为空Frist< T >
获取数组类型
T的第一个元素的类型,如果为空数组,则返回never
一、
js
/* _____________ 你的代码 _____________ */
// T extends [] 如果类型为空数组
type First<T extends any[]> = T extends [] ? never : T[0];
/* _____________ 测试用例 _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<First<[3, 2, 1]>, 3>>,
Expect<Equal<First<[() => 123, { a: string }]>, () => 123>>,
Expect<Equal<First<[]>, never>>,
Expect<Equal<First<[undefined]>, undefined>>,
]
type errors = [
// @ts-expect-error
First<'notArray'>,
// @ts-expect-error
First<{ 0: 'arrayLike' }>,
]二、
js
type First<T extends any[]> = T extends [infer A, ...infer rest] ? A : never三、
js
// 如果T的长度为0,则无效匹配,否则取第一个元素类型
type First<T extends any[]> = T['length'] extends 0 ? never : T[0]获得元组长度
js
/* _____________ 你的代码 _____________ */
type Length<T extends readonly any[]> = T['length']
/* _____________ 测试用例 _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
const tesla = ['tesla', 'model 3', 'model X', 'model Y'] as const
const spaceX = ['FALCON 9', 'FALCON HEAVY', 'DRAGON', 'STARSHIP', 'HUMAN SPACEFLIGHT'] as const
type cases = [
Expect<Equal<Length<typeof tesla>, 4>>,
Expect<Equal<Length<typeof spaceX>, 5>>,
// @ts-expect-error
Length<5>,
// @ts-expect-error
Length<'hello world'>,
]Exclude<T, U>
T中联合类型排除U类型
js
/* _____________ 你的代码 _____________ */
// T中如果有U这个类型,那就设置为never,否则类型就留着
type MyExclude<T, U> = T extends U ? never : T;
/* _____________ 测试用例 _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<MyExclude<'a' | 'b' | 'c', 'a'>, 'b' | 'c'>>,
Expect<Equal<MyExclude<'a' | 'b' | 'c', 'a' | 'b'>, 'c'>>,
Expect<Equal<MyExclude<string | number | (() => void), Function>, string | number>>,
]js
MyExclude<'a' | 'b' | 'c', 'a' | 'b'>
= ('a' extends 'a' | 'b' ? never : 'a') → never
| ('b' extends 'a' | 'b' ? never : 'b') → never
| ('c' extends 'a' | 'b' ? never : 'c') → 'c'
→ 最终结果是 'c'获得Promise< T>中的类型
js
/* _____________ 你的代码 _____________ */
/**
* 1. PromiseLike:传入类型为Promise,并且有.then()方法;如果是Promise,那么是需要完整的Promise(.then、catch、.finally);
* 2. 限制传入的类型:T extends PromiseLike<any>
* 3. 获得传入的类型:T extends PromiseLike<infer X>
* 4. 如果传入的类型还是一个PromiseLike类型,那么在进行递归,否则返回Promise类型X;
* 5. 如果传入的Promise没有类型,那么返回never
*/
type MyAwaited<T extends PromiseLike<any>> = T extends PromiseLike<infer X> ? X extends PromiseLike<any> ? MyAwaited<X> : X : never
/* _____________ 测试用例 _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type X = Promise<string>
type Y = Promise<{ field: number }>
type Z = Promise<Promise<string | number>>
type Z1 = Promise<Promise<Promise<string | boolean>>>
type T = { then: (onfulfilled: (arg: number) => any) => any }
type cases = [
Expect<Equal<MyAwaited<X>, string>>,
Expect<Equal<MyAwaited<Y>, { field: number }>>,
Expect<Equal<MyAwaited<Z>, string | number>>,
Expect<Equal<MyAwaited<Z1>, string | boolean>>,
Expect<Equal<MyAwaited<T>, number>>,
]If
js
type If<C extends boolean, T , F > = C extends true ? T : F
/* _____________ 测试用例 _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<If<true, 'a', 'b'>, 'a'>>,
Expect<Equal<If<false, 'a', 2>, 2>>,
Expect<Equal<If<boolean, 'a', 2>, 'a' | 2>>,
]
// @ts-expect-error
type error = If<null, 'a', 'b'>unknown
表示这个变量的类型还不确定,你不能直接对它做任何操作,除非你先做类型缩小(类型检查)。
js
let a: any = 123;
a.toFixed(); // ✅ 可以调用任何方法,哪怕这个值其实是错误的
let b: unknown = 123;
// b.toFixed(); ❌ 报错,必须先做类型检查
if (typeof b === 'number') {
b.toFixed(); // ✅ 现在可以用了
}Array.concat
类似于Array.concat作用,把2个数组的类型合并
js
/*
533 - Concat
-------
by Andrey Krasovsky (@bre30kra69cs) #简单 #array
### 题目
在类型系统里实现 JavaScript 内置的 `Array.concat` 方法,这个类型接受两个参数,返回的新数组类型应该按照输入参数从左到右的顺序合并为一个新的数组。
例如:
```ts
type Result = Concat<[1], [2]> // expected to be [1, 2]在 Github 上查看:https://tsch.js.org/533/zh-CN > */
/_ **_** 你的代码 **_** _/
type Concat<T extends readonly unknown[], U extends readonly unknown[]> = [...T,...U]
/_ **_** 测试用例 **_** _/ import type { Equal, Expect } from '@type-challenges/utils'
const tuple = [1] as const
type cases = [ Expect<Equal<Concat<[], []>, []>>, Expect<Equal<Concat<[], [1]>, [1]>>, Expect<Equal<Concat<typeof tuple, typeof tuple>, [1, 1]>>, Expect<Equal<Concat<[1, 2], [3, 4]>, [1, 2, 3, 4]>>, Expect<Equal<Concat<['1', 2, '3'], [false, boolean, '4']>, ['1', 2, '3', false, boolean, '4']>>, ]
## Equal
```js
// 判断2个参数的泛型函数返回类型是否相等,如果相等则2个值相等
type isEqual<A,B> = (<T>() => T extends A ? 1 : 2) extends (<T>() => T extends B ? 1 : 2) ? true : false;数组遍历
js
// 获取数组第一个元素以及其余元素,还可以用于数组判空
T extends [infer Frist, ...infer Rest]对象遍历
js
//需要用到keyof属性,keyof属性是获取某种类型的所有建
type Objcet = {
name:string,
age:number
}
type o = keyof Object; // string | number
// 那么就可以进行遍历
type o1 = {
[it in keyof Object]:Objcet[it]
}Include
js
*/
/* _____________ 你的代码 _____________ */
/**
* 遍历数组
* 比较元素
*/
type isEqual<A,B> = (<T>() => T extends A ? 1 : 2) extends (<T>() => T extends B ? 1 : 2) ? true : false;
type Includes<T extends readonly any[], U> = T extends [infer Frist, ...infer Rest] ? isEqual<U,Frist> extends true ? true : Includes<Rest,U> : false
/* _____________ 测试用例 _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Kars'>, true>>,
Expect<Equal<Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'>, false>>,
Expect<Equal<Includes<[1, 2, 3, 5, 6, 7], 7>, true>>,
Expect<Equal<Includes<[1, 2, 3, 5, 6, 7], 4>, false>>,
Expect<Equal<Includes<[1, 2, 3], 2>, true>>,
Expect<Equal<Includes<[1, 2, 3], 1>, true>>,
Expect<Equal<Includes<[{}], { a: 'A' }>, false>>,
Expect<Equal<Includes<[boolean, 2, 3, 5, 6, 7], false>, false>>,
Expect<Equal<Includes<[true, 2, 3, 5, 6, 7], boolean>, false>>,
Expect<Equal<Includes<[false, 2, 3, 5, 6, 7], false>, true>>,
Expect<Equal<Includes<[{ a: 'A' }], { readonly a: 'A' }>, false>>,
Expect<Equal<Includes<[{ readonly a: 'A' }], { a: 'A' }>, false>>,
Expect<Equal<Includes<[1], 1 | 2>, false>>,
Expect<Equal<Includes<[1 | 2], 1>, false>>,
Expect<Equal<Includes<[null], undefined>, false>>,
Expect<Equal<Includes<[undefined], null>, false>>,
]Push
js
/* _____________ 你的代码 _____________ */
type Push<T extends unknown[], U> = [...T,U]
/* _____________ 测试用例 _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<Push<[], 1>, [1]>>,
Expect<Equal<Push<[1, 2], '3'>, [1, 2, '3']>>,
Expect<Equal<Push<['1', 2, '3'], boolean>, ['1', 2, '3', boolean]>>,
]Unshift
js
/* _____________ 你的代码 _____________ */
type Unshift<T extends unknown[], U> = [U,...T]
/* _____________ 测试用例 _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<Unshift<[], 1>, [1]>>,
Expect<Equal<Unshift<[1, 2], 0>, [0, 1, 2]>>,
Expect<Equal<Unshift<['1', 2, '3'], boolean>, [boolean, '1', 2, '3']>>,
]Parameters
提取函数中的参数形成元组
js
/* _____________ 你的代码 _____________ */
// T extends (...args: any[]) => any,限定参数类型为函数,返回值为any
// T extends (...args:infer P) => unknown 类型推导和extends判断,是否有参数,如果有参数则返回...args的推导类型,没有参数则为never
type MyParameters<T extends (...args: any[]) => any> = T extends (...args:infer P) => unknown ? P : never
/* _____________ 测试用例 _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
function foo(arg1: string, arg2: number): void {}
function bar(arg1: boolean, arg2: { a: 'A' }): void {}
function baz(): void {}
type cases = [
Expect<Equal<MyParameters<typeof foo>, [string, number]>>,
Expect<Equal<MyParameters<typeof bar>, [boolean, { a: 'A' }]>>,
Expect<Equal<MyParameters<typeof baz>, []>>,
]Record<K, T>
map 使用
- K 表示的是:创建的新对象需要有哪些属性,属性可以只有一个,也可以有多个,多个属性时采用“联合类型”的写法
- T 表示的是:对象属性的类型。
Promise
Promise返回值只计算resolve类型,
reject(...)的值不会影响Promise<T>的泛型
ts
/**
* 检查敏感词
* @param word - 需要检查的词汇
* @returns {Promise<boolean>} - 是否包含敏感词(true-包含 | false-不包含)
* @throws {string} - 检查发生错误
*/
export async function checkSensitivityWord(word: string): Promise<Boolean> {
try {
let res = await self_request('/mztSensitiveWords/check', 'POST', { word });
console.log(res, 'res');
return !res?.data;
} catch (e) {
throw new Error('检查发生错误');
}
}error TS2339: Property 'entries' does not exist on type 'FormData'
json
{
"compilerOptions": {
"baseUrl": ".",
"outDir": "build/dist",
"module": "esnext",
"target": "es6",
"lib": [
"es2019",
"dom",
"dom.iterable" // <- there you are
],
// ...
}