Better

Ethan的博客,欢迎访问交流

TypeScript 细节巩固和 cheatsheet

看到一个不错的文章,顺带结合自己当前使用需求,再次过了一遍文档,巩固一下细节

文档

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;
}

资料



留言