Typescript有什么冷门但是很好用的特性?

typescript基本上常用的特性,除了es6+的各种语法糖就是接口、命名空间这些特性了,还有其他优秀的特性值得分享吗?
关注者
694
被浏览
83,655

23 个回答

高级类型

type Readonly<T> = {
    readonly [P in keyof T]: T[P];
type Partial<T> = {
    [P in keyof T]?: T[P];
type Nullable<T> = {
  [P in keyof T]: T[P] | null;
type Proxy<T> = {
    get(): T;
    set(value: T): void;
type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
type Record<K extends string, T> = {
    [P in K]: T;
type Diff<T extends string, U extends string> =
    ({[P in T]: P } & {[P in U]: never } & { [x: string]: never })[T];  
type Overlap<T extends string, U extends string> = Diff<T, Diff<T, U>>;
type Omit<T, K extends keyof T> = Pick<T, Diff<keyof T, K>>;
type Overwrite<T, U> = Omit<T, Diff<keyof T, Diff<keyof T, keyof U>>> & U;
type Purify<T extends string> = { [P in T]: T; }[T];
type NonNullable<T> = T & {};
type Required<T> = {
  [P in Purify<keyof T>]: NonNullable<T[P]>;
};

在 TS 2.8 +

type Omit = Pick<T, Exclude<keyof T, K>>

在 React JSX 语法中 cast 类型

假设我们已经有了这样一个组件:

export default class Column<T> extends React.Component<ColumnProps<T>, React.ComponentState> {}

因为在 JSX 语法中我们没法 cast 一个具体的 type 给 Column,所以我会这样

class MyColumn extends Column<{
  userId?: number,
  docId?: string,
  title?: string,
}> {}

之后用 MyColumn 代替 Column 组件


重载在官方文档里已经有了,现在是如何为一个重载的函数写高阶函数?

假设我们有重载函数 fn:

declare function fn(r: () => string): string;
declare function fn(r: () => number): number;

这个高阶函数可以这样写:

function myFn(r: () => string): string;
function myFn(r: () => number): number;
function myFn(r: (() => string) | (() => number)): string | number {
    type R = ReturnType<typeof r> extends string ? string : number
    return fn(r as any) as any as R

其实,这里我承认,在 myFn 里取到 R 没有什么意义,直接 return fn(r as any) 就可以了,但手动 cast myFn 的返回类型,人生就变成彩色的了呢!


如何调用静态方法,并且可以灵活地修改类名?

在 JavaScript 里很简单,Foo 可以随时改(在 VS Code 里也很简单,rename symbol 即可)

class Foo {
    static bar = 'Oops!'
    getBarLength () {
        return this.constructor.bar.length
    getBarUpperCase () {
        return this.constructor.bar.toUpperCase()

但在 TypeScript 里你可以要忍受重复书写 Foo 的类名:

class Foo {
    static bar = 'Oops!'
    getBarLength () {
        return (this.constructor as typeof Foo).bar.length
    getBarUpperCase () {
        return (<typeof Foo>this.constructor).bar.toUpperCase()

那么小技巧来了:

class Foo {
    'constructor': typeof Foo
    static bar = "Oops!"
    getBarLength () {
        return this.constructor as Foo).bar.length
    getBarUpperCase () {
        return this.constructor.bar.toUpperCase()

感受自由吧


为函数添加静态属性

在 Class 里 static 关键字给予了我们这种能力,在 JavaScript 中,它也不在话下:

function test () {}
test.cache = {}

仅仅用 TS 给 test 加个签名分发给其它人用,也不成问题:

// xx.d.ts
interface Test {
    (): void,
    cache: object,
declare const test: Test
export default test

But, 怎么在 TS 中给函数加上静态属性?

先来个丑陋的(我不用):

interface Test {
    (): void,
    cache: object,
const test = (function () { }) as Test
test.cache = {}

或者 IIFE:

interface Test {
    (): void,
    cache: object,
const test: Test = (function () {
    function test() { }
    (test as any).cache = {}
    return test as Test
export default test

人生苦短,我用 namespace:

function test () {}