类型定义
Record
高级对象用法
Record 源码
/**
* Construct a type with a set of properties K of type T
*/
type Record<K extends keyof any, T> = {
[P in K]: T;
};
以数字作为key
interface EmployeeType {
id: number
fullname: string
role: string
}
// 将employees的value值的类型定义为EmployeeType
let employees: Record<number, EmployeeType> = {
0: { id: 1, fullname: "John Doe", role: "Designer" },
1: { id: 2, fullname: "Ibrahima Fall", role: "Developer" },
2: { id: 3, fullname: "Sara Duckson", role: "Developer" },
}
以枚举作为key
type petsGroup = 'dog' | 'cat' | 'fish';
interface IPetInfo {
name:string,
age:number,
}
type IPets = Record<petsGroup | 'turtle', IPetInfo>;
// 数据格式
const animalsInfo:IPets = {
dog:{
name:'dogName',
age:2
},
cat:{
name:'catName',
age:3
},
fish:{
name:'fishName',
age:5
}
turtle:{.../}
}
IPets 类型是由 Record
<petsGroup, IPetInfo>
返回的。将petsGroup中的每个值(dog
|cat
|fish
)都转为 IPetInfo 类型。
axios用来定义各个request请求的方法
// 枚举除了几个常见的http请求的方法名
enum IHttpMethods { GET = 'get', POST = 'post', DELETE = 'delete', PUT = 'put' }
// 每个方法都接受请求的url以及可选参数config, 返回的都是一个Promise
interface IHttpFn { <T = any>(url: string, config?: AxiosRequestConfig): Promise<T> }
// 定义
type IHttp = Record<IHttpMethods, IHttpFn>;
// 遍历所有方法,对每个方法有各自的具体实现即可。
export default const httpMethods: IHttp = ["get", "post", "delete", "put"].reduce((map: any, method: string) => {
map[method] = (url: string, options: AxiosRequestConfig = {}) => {
const { data, ...config } = options;
return (axios as any)[method](url, data, config)
.then((res: AxiosResponse) => {
if (res.data.errCode) {
//todo somethins
} else {
//todo somethins
}
});
}
return map
}, {})
类型定义
定义某个对象的key为数组内容
const obj:{}
预先定义空变量
- 场景
// 类似这样新建一个空对象是经常使用的手法,当然在传统ts项目中不再推荐
let person = {}
// 如果要添加属性
person.name = "xxx" // error 此处ts无法推荐该类型,大概会报错
- 解决
//1 使用 interface
interface Person {
name:string
age:number
}
let person = {} as Person
person.name="capsion"
//2 跳过类型检测(不推荐)
let person = {} as any
person.name="capsion"
全局变量的扩展(window、global、process)
// 首先先声明全局存在这个东西
interface window {
ccvb: string
}
// node下 global 和 process
// 位于 lib.d.ts 在得知其命名空间和结构
declare namespace NodeJS {
export interface Process {
ccvb?: any;
}
}
declare namespace NodeJS {
export interface Process {
ccvb?: any;
}
}
process.ccvb = "ccvb";
字符串
字符串的声明比较简单
let name:string = "xxxxx"
数字
枚举
方式1
export enum CardSuit {
UNKNOWN = '',
PASSPORT_VISA = 'passport_visa',
PASSPORT = 'passport',
SIGHTED_STUDENT_CARD = 'sighted_tertiary_edu_id',
SIGHTED_KEYPASS_CARD = 'sighted_keypass_card',
SIGHTED_PROOF_OF_AGE_CARD = 'sighted_proof_of_age_card'
}
方式2
export type DocsVersionPersistence = 'localStorage' | 'none';
数组
提取数组元素类型
使用as const
利用 TypeScript 的类型推导特性,将数组声明为只读常量数组(使用 as const
),然后通过索引类型查询来自动提取出联合类型,避免手动写入所有字符串。这种方式更加简洁且易于维护。
const directions = ["right", "rightTop", "top", "leftTop", "left", "leftBottom", "bottom", "rightBottom"] as const;
export type DirectionT = typeof directions[number];
type Animals = Array<{ type: string, name: string }>;
type Animal = Animals[number];
// 使用 infer
type Animal = Animals extends (infer T)[] ? T : never;
对象
函数
你可以注解函数参数,就像你可以注解其他变量一样:
函数声明:
- 方式1:
type LongHand = {
(a: number): number;
};
- 方式2:
type ShortHand = (a: number) => number;
两种方式完全等价
函数重载:
让同一个函数支持不同的入参类型和返回类型
// 只有第一种声明方式支持重载
type LongHandAllowsOverloadDeclarations = {
(a: number): number;
(a: string): string;
};
- 应用
// 这里定义了这是一个多态的接口
interface Overloaded {
(foo: string): string;
(foo: number): number;
}
// 函数重载
// 这里对接口进行逐一实现
// 实现接口的一个例子:
function stringOrNumber(foo: number): number;
function stringOrNumber(foo: string): string;
function stringOrNumber(foo: any): any {
if (typeof foo === 'number') {
return foo * foo;
} else if (typeof foo === 'string') {
return `hello ${foo}`;
}
}
const overloaded: Overloaded = stringOrNumber;
// 使用
const str = overloaded(''); // str 被推断为 'string'
const num = overloaded(123); // num 被推断为 'number'
参数声明:
- 内联类型注解
// variable annotation
let sampleVariable: { bar: number };
// function parameter annotation
function foo(sampleParameter: { bar: number }) {}
- 返回类型注解
interface Foo {
foo: string;
}
// Return type annotated as `: Foo`
function foo(sample: Foo): Foo {
return sample;
}
- 可选参数
function foo(bar: number, bas?: string): void {
// ..
}
foo(123);
foo(123, 'hello');
- 默认值参数
function foo(bar: number, bas: string = 'hello') {
console.log(bar, bas);
}
foo(123); // 123, hello
foo(123, 'world'); // 123, world
箭头函数
- 基础注解
const simple: (foo: number) => string = foo => foo.toString();
箭头函数无法使用重载,因为它无法被具体的指定
类
- 普通类的声明
// 类的定义
interface Point {
x: number;
y: number;
}
// 使用 implements 指定类型
class MyPoint implements Point {
x: number;
y: number; // Same as Point
}
带泛型的类
class List<T> {
add(val: T){}
}
class Target{
name:string
}
// 实例化的同时将对应的类型传入
const obj = new List<Target>()
完整带泛型的事件类示例:
export interface Listener<T> {
(event: T): any;
}
export interface Disposable {
dispose(): any;
}
export class TypedEvent<T> {
private listeners: Listener<T>[] = [];
private listenersOncer: Listener<T>[] = [];
public on = (listener: Listener<T>): Disposable => {
this.listeners.push(listener);
return {
dispose: () => this.off(listener)
};
};
public once = (listener: Listener<T>): void => {
this.listenersOncer.push(listener);
};
public off = (listener: Listener<T>) => {
const callbackIndex = this.listeners.indexOf(listener);
if (callbackIndex > -1) this.listeners.splice(callbackIndex, 1);
};
public emit = (event: T) => {
this.listeners.forEach(listener => listener(event));
this.listenersOncer.forEach(listener => listener(event));
this.listenersOncer = [];
};
public pipe = (te: TypedEvent<T>): Disposable => {
return this.on(e => te.emit(e));
};
}
模块
接口
接口的用户非常多,而任结构的类型都可以通过接口来定义
// 基础接口
interface name {
name:string
sex:string
run?:boolean - 可参数
}
let newName:name = { name:'ccvb', sex:'male' }
元素
命名空间
//声明
namespace name{
export class person{
public name:string="ccvb"
show:void(){
console.log('ccvb')
}
}
}
// 使用
let c:name.person = new name.person()
泛型
约定:
- T(Type):表示一个
TypeScript
类型 - K(Key):表示对象中的键类型
- V(Value):表示对象中的值类型
- E(Element):表示元素类型
- 泛型接口
interface GenericIdentityFn<T> {
(arg: T): T;
}
- 泛型类
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function (x, y) {
return x + y;
};
捕获类型(typeof)
- 捕获一个变量的类型
let name:string = 'ccvb'
let new_name:typeof name = 'capsion'
- 获取一个函数的类型
// 定义一个类型
// 这是一个 (x:number)=>number[] 类型
function toArray(x: number): Array<number> {
return [x];
}
// 获取上面函数的类型
const toArrayType = typeof toArray
- 捕获一个类成员的类型
class Person {
name:string
}
declare let person:Person
let new_person: typeof person.name
- 配合
keyof
捕获对应健的类型
const colors = {
red: 'red',
blue: 'blue'
};
// keyof 返回的是一个数组联合类型
type Colors = keyof typeof colors;
// 限制某个变量的值必须是某个对对象的键名
let color: Colors; // color 的类型是 'red' | 'blue'
color = 'red'; // ok
color = 'blue'; // ok
color = 'anythingElse'; // Error
keyof
应用场景:
- 动态的生成一个联合类型
- 这允许你很容易地拥有像字符串枚举+常量这样的类型
const coords3D = {
x:0,
y:0,
z:0
}
// 限制一个元素的值必须是 coords3D 中的其中一个
let Coords3D = keyof typeof coords3D
// 此时 target 的值被限制必须是 coords3D 的键值
let target:Coords3D
target = 'x' // ok
target = 'y' // ok
target = 'z' // ok
target = 'a' // error
声明一个索引签名
应用场景:
强制约素对象或类所有成员属性的类型都是一致的
// ok
interface Foo {
[key: string]: number;
x: number;
y: number;
}
// Error
interface Bar {
[key: string]: number;
x: number;
y: string; // Error: y 属性必须为 number 类型
}
通过映射类型来使索引字符串必须联合类型中的一员
type Index = 'a' | 'b' | 'c';
type FromIndex = { [k in Index]?: number };
const good: FromIndex = { b: 1, c: 2 };
// Error:
// `{ b: 1, c: 2, d: 3 }` 不能分配给 'FromIndex'
// 对象字面量只能指定已知类型,'d' 不存在 'FromIndex' 类型上
const bad: FromIndex = { b: 1, c: 2, d: 3 };
工具类型/Required<>
- 可选类型转必选
用于将一个对象类型中的所有可选属性(?
修饰的属性)转换为必选属性。它是 TypeScript 类型系统中非常实用的工具,尤其在处理需要确保属性必然存在的场景时非常有用。
语法:
type Required<T> = {
[P in keyof T]-?: T[P];
};
interface User {
id?: number;
name?: string;
}
type RequiredUser = Required<User>;
/* 等效于:
interface RequiredUser {
id: number; // 从可选变为必选
name: string; // 从可选变为必选
} */
场景1:默认props
// 外部暴露的props类型都是可选的,让外部调用组件更灵活
interface GlowBackgroundProps {
glowColor?: string; // 光晕颜色
scaleRange?: [number, number]; // 光晕尺寸比例(相对于视口)
blur?: number; // 模糊度
baseSize?: number;
parallaxIntensity?: number; // 视差强度
count?: number;
}
// 内部先指定一个默认值的props,确保类型是目标props的所有可选类型转换成必选类型的形式
const DEFAULT_PROPS: Required<GlowBackgroundProps> = { glowColor: "#FC1E4F", scaleRange: [0.8, 2], blur: 10, baseSize: 200, count: 3, parallaxIntensity: 1 };
export default function GlowBackground(_props: GlowBackgroundProps = {}) {
const props: Required<GlowBackgroundProps> = { ...DEFAULT_PROPS, ..._props };
return (
// 在这里使用props不会在被ts提示属性可能为空的尴尬
)
}