看到一个不错的文章,顺带结合自己当前使用需求,再次过了一遍文档,巩固一下细节
文档
typescript 的高级 typing 还是很强的
- 从类型创建类型,太他妈强了
- generics:理解泛型是理解更多高级工具的关键
- keyof:配合 in 关键字,还能实现遍历效果
- typeof:typeof data、typeof ArrayInstance[number] 获取数组元素类型
- indexed:Type[key]、Type[key | key2]
- 条件类型:extends,infer 关键字可以从 true 分支中提取类型
- 映射类型:解决重复问题,基于一个类型生成另一个类型,需要上面关键字的帮助,比如 keyof 遍历所有 key 来生成,还有其他小语法,比如通过
+
、-
号作用于 readonly 和?
上进行类型修改,或者 as 关键字生成新类型 key,比如[key in keyof Type as
${key}Changed]
- 模板字面量类型:type Event =
${key}Changed
,key 如果是联合字面量类型,则返回的也是联合类型,内在的字符串操作类型,Uppercase/LowercaseCapitalize/Uncapitalize
- 已有的工具函数
- ……
- Parameters
返回函数的参数类型元组类型 - ConstructorParameters
构造函数参数类型元组或数组 - ReturnType
函数返回值类型
- 特殊类型妙用
- any:任意类型,不受约束,不推荐使用
- unknown:所有类型都可以分配给 unknown,必须在判断后才能继续使用,推荐使用。当你想用 any 时,考虑用它,更安全
- never:用于永远不可能发生的情况,主要用于错误检查或收敛条件类型,比如你期望函数中会处理所有的条件判断,这样 else 语句应该永远不会执行到,这时候你可以考虑用 never 类型进行赋值,如果条件判断不全,则编译器会给出错误。或作为泛型参数的默认值去支持内部逻辑处理,或者与 infer 和条件语句结合,实现过滤某些属性
- 其他
- 非空断言
!
,你确定不会为空时可用,减少 if 判断,但不要乱用,并不安全哦 - const 断言:as const 方式指定对象里内容都是常量类型,不允许修改
- 非空断言
细节演示
泛型进阶示例
function getProperty<Type, Key extends keyof Type>(obj: Type, key: Key) {
return obj[key];
}
function create<Type>(c: { new (): Type }): Type {
return new c();
}
never 举例,如果某天不小心扩展了 a 的类型,但并没有做对应的 if 判断,这时候编译器就会给出提示,以及 never 在工具函数中的使用。
function test(a: string | number) {
if (typeof a === 'string') {
return a.concat('world');
}
if (typeof a === 'number') {
return a + 100;
}
const neverCheck: never = a;
return neverCheck
}
type Parameters<T extends (...args: any[]) => any> = T extends (...args: infer P) => any ? P : never;
type Exclude<T, U> = T extends U ? never : T;
type Extract<T, U> = T extends U ? T : never;
api 中心化管理,给了我启发,因为 ajax 返回值类型每次都要手动声明,很难形成约束,这样第一可以统一管理,第二自动形成了约束,秒哉。
interface API {
'/user': { name: string },
'/menu': { list: string[] }
}
常见我们对泛型的常见命名,按照惯例,类型参数名称是单个大写字母,区分类型变量和普通类或接口名称之间的区别:
- T(Type): 最常用的类型参数名
- K(Key): 对象的键类型
- V(Value): 对象的值类型
- P(Property): 对象的属性类型
- R(Result): 类型推导的结果类型
interface 定义函数或构造函数
(s: string): User;
new(s: string): User;
cheatsheet
/**
* An events map is an interface that maps event names to their value, which
* represents the type of the `on` listener.
*/
export interface EventsMap {
[event: string]: any;
}
/**
* The default events map, used if no EventsMap is given. Using this EventsMap
* is equivalent to accepting all event names, and any data.
*/
export interface DefaultEventsMap {
[event: string]: (...args: any[]) => void;
}
/**
* Returns a union type containing all the keys of an event map.
*/
export type EventNames<Map extends EventsMap> = keyof Map & (string | symbol);
/** The tuple type representing the parameters of an event listener */
export type EventParams<
Map extends EventsMap,
Ev extends EventNames<Map>
> = Parameters<Map[Ev]>;
/**
* The event names that are either in ReservedEvents or in UserEvents
*/
export type ReservedOrUserEventNames<
ReservedEventsMap extends EventsMap,
UserEvents extends EventsMap
> = EventNames<ReservedEventsMap> | EventNames<UserEvents>;
/**
* Type of a listener of a user event or a reserved event. If `Ev` is in
* `ReservedEvents`, the reserved event listener is returned.
*/
export type ReservedOrUserListener<
ReservedEvents extends EventsMap,
UserEvents extends EventsMap,
Ev extends ReservedOrUserEventNames<ReservedEvents, UserEvents>
> = FallbackToUntypedListener<
Ev extends EventNames<ReservedEvents>
? ReservedEvents[Ev]
: Ev extends EventNames<UserEvents>
? UserEvents[Ev]
: never
>;
/**
* Returns an untyped listener type if `T` is `never`; otherwise, returns `T`.
*
* This is a hack to mitigate https://github.com/socketio/socket.io/issues/3833.
* Needed because of https://github.com/microsoft/TypeScript/issues/41778
*/
type FallbackToUntypedListener<T> = [T] extends [never]
? (...args: any[]) => void | Promise<void>
: T;
/**
* Strictly typed version of an `EventEmitter`. A `TypedEventEmitter` takes type
* parameters for mappings of event names to event data types, and strictly
* types method calls to the `EventEmitter` according to these event maps.
*
* @typeParam ListenEvents - `EventsMap` of user-defined events that can be
* listened to with `on` or `once`
* @typeParam EmitEvents - `EventsMap` of user-defined events that can be
* emitted with `emit`
* @typeParam ReservedEvents - `EventsMap` of reserved events, that can be
* emitted by socket.io with `emitReserved`, and can be listened to with
* `listen`.
*/
export class Emitter<
ListenEvents extends EventsMap,
EmitEvents extends EventsMap,
ReservedEvents extends EventsMap = {}
> {
/**
* Adds the `listener` function as an event listener for `ev`.
*
* @param ev Name of the event
* @param listener Callback function
*/
on<Ev extends ReservedOrUserEventNames<ReservedEvents, ListenEvents>>(
ev: Ev,
listener: ReservedOrUserListener<ReservedEvents, ListenEvents, Ev>
): this;
/**
* Adds a one-time `listener` function as an event listener for `ev`.
*
* @param ev Name of the event
* @param listener Callback function
*/
once<Ev extends ReservedOrUserEventNames<ReservedEvents, ListenEvents>>(
ev: Ev,
listener: ReservedOrUserListener<ReservedEvents, ListenEvents, Ev>
): this;
/**
* Removes the `listener` function as an event listener for `ev`.
*
* @param ev Name of the event
* @param listener Callback function
*/
off<Ev extends ReservedOrUserEventNames<ReservedEvents, ListenEvents>>(
ev?: Ev,
listener?: ReservedOrUserListener<ReservedEvents, ListenEvents, Ev>
): this;
/**
* Emits an event.
*
* @param ev Name of the event
* @param args Values to send to listeners of this event
*/
emit<Ev extends EventNames<EmitEvents>>(
ev: Ev,
...args: EventParams<EmitEvents, Ev>
): this;
/**
* Emits a reserved event.
*
* This method is `protected`, so that only a class extending
* `StrictEventEmitter` can emit its own reserved events.
*
* @param ev Reserved event name
* @param args Arguments to emit along with the event
*/
protected emitReserved<Ev extends EventNames<ReservedEvents>>(
ev: Ev,
...args: EventParams<ReservedEvents, Ev>
): this;
/**
* Returns the listeners listening to an event.
*
* @param event Event name
* @returns Array of listeners subscribed to `event`
*/
listeners<Ev extends ReservedOrUserEventNames<ReservedEvents, ListenEvents>>(
event: Ev
): ReservedOrUserListener<ReservedEvents, ListenEvents, Ev>[];
/**
* Returns true if there is a listener for this event.
*
* @param event Event name
* @returns boolean
*/
hasListeners<
Ev extends ReservedOrUserEventNames<ReservedEvents, ListenEvents>
>(event: Ev): boolean;
/**
* Removes the `listener` function as an event listener for `ev`.
*
* @param ev Name of the event
* @param listener Callback function
*/
removeListener<
Ev extends ReservedOrUserEventNames<ReservedEvents, ListenEvents>
>(
ev?: Ev,
listener?: ReservedOrUserListener<ReservedEvents, ListenEvents, Ev>
): this;
/**
* Removes all `listener` function as an event listener for `ev`.
*
* @param ev Name of the event
*/
removeAllListeners<
Ev extends ReservedOrUserEventNames<ReservedEvents, ListenEvents>
>(ev?: Ev): this;
}