【问题标题】:How to extend a type in a type formula in TypeScript?如何在 TypeScript 的类型公式中扩展类型?
【发布时间】:2019-07-20 15:10:10
【问题描述】:

当你看到标题时,你可能会马上回答“interface A extends B { ... }”,但这不是我想要的。

我的意思是,我有两种类型(例如 A 和 B),我想得到一个类型 C,它具有 A 的所有成员和 B 的成员与 A 的成员具有不同的名称,以及类型A 的成员应该可以分配给 B 的成员。

这一切都与interface A extends B { ... }的行为相同,只是必须通过类型公式获取。

换句话说,我想要这样的东西:

interface C extends B {
    [K in keyof A]: A[K];
    // this is invalid because members of interfaces must be statically determined in TS
}
// And I don't want an interface.

或者像这样:

// About Omit:
// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-5.html#the-omit-helper-type
type C = Omit<B, keyof A> & A;
// this can be invalid when B has
//     [k: string]: any;

我想知道我是否可以得到如下形式的C型:

type A = { ... }
type B = { ... }
type C = A formula containing A and B

【问题讨论】:

  • 如果B 有索引签名,结果应该是什么?索引签名通常不能很好地与映射类型一起使用,但我知道某处有一种解决方法..

标签: typescript


【解决方案1】:

您的版本type C = Omit&lt;B, keyof A&gt; &amp; A; 对我来说似乎不错,索引签名的警告是映射类型的普遍问题。

您可以使用this workaround 获取类型的已知键。有了这个,我们可以首先选择已知的键,然后选择任何索引string 索引签名和与A 的交集:

type KnownKeys<T> = {
  [K in keyof T]: string extends K ? never : number extends K ? never : K
} extends { [_ in keyof T]: infer U } ? U : never;

type A = {
    a: number;
    b: string;
}

type B = {
    [a: string]: any;    
    d: number;
    b: string;
}

type C = Pick<B, Exclude<KnownKeys<B>, keyof A>> & Pick<B, string> & A;
declare let c: C;
c.d // d is still here
c[""] // index is still here

或者更通用的版本:

type KnownKeys<T> = {
  [K in keyof T]: string extends K ? never : number extends K ? never : K
} extends { [_ in keyof T]: infer U } ? U : never;

type A = {
    a: number;
    b: number;
}

type B = {
    [a: string]: any;    
    d: number;
    b: string;
}

type Merge<T, U> = 
    // Picking the known keys from T, requires the introduction of K as a new type parameter
    // We do this in order to make TS know K is a keyof T on the branch we do the pick
    (Exclude<KnownKeys<T>, keyof U> extends infer K? K extends keyof T ? Pick<T, K> : never: never )
    // Pick the string index signature if any 
    & (T extends Record<string, any> ? Pick<T, string> : never) 
    // We can also pick the numeric index
    & (T extends Record<number, any> ? Pick<T, number> : never) 
    // Intersect with U 
    & U;
declare let c: Merge<B, A>;
c.d // d is still here
c[""] // index is still here`

【讨论】:

    猜你喜欢
    • 2018-01-03
    • 2021-03-25
    • 1970-01-01
    • 2016-08-02
    • 2018-05-08
    • 2021-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多