具有动态静态场的功能

时间:2019-12-03 09:26:37

标签: typescript typescript-typings typing

我需要使用泛型类型的动态静态字段为函数编写类型

interface WithStatic<P, T extends string[]> {
  (props: P): void;
  [K in T]: WithStatic<P, T>; // TS Error: A computed property name must be of type 'string', 'number', 'symbol', or 'any'.
}

Playground link

如果我将动态字段移得更深(将其移至另一个字段),则一切正常:

interface WithStatic<P, T extends string> {
  (props: P): void;
  options: {
    [K in T]: WithStatic<P, T>;
  };
}

Playground link

用法示例:

const withStatic = (props => {}) as WithStatic<{}, 'maybe' | 'ready'>;

然后键入

withStatic.options.

显示maybeready

enter image description here

我该如何在接口的根级别定义动态类型?

修改

Playground link what I got in the result

1 个答案:

答案 0 :(得分:3)

除了映射表达式(即[K in T]: ...)外,映射类型不能包含任何其他声明。而且它也不会出现在界面中。

考虑到这一点,您将需要在类型别名中使用交集来描述所需的类型。这将起作用:

type WithStatic<P, T extends string[]> = {
  (props: P): void;
} & {
  [K in T[number]]: WithStatic<P, T>; 
}

let c = ((p: {}) => 1) as any as WithStatic<{}, ['maybe', 'ready']>

c.maybe.maybe // not sure this is what you are going for here but it compiles..

Playground Link

虽然上面的类型有效,但我不确定它是否真的有意义,也许您希望在T中声明的字段为特定类型(例如stringanyboolean),则可以只使用映射类型Record。同样,我们通常将多个键作为联合而不是作为元组传递:

type WithStatic<P, T extends string> = {
  (props: P): void;
} & Record<T, string> // replace strign with whatever 

let c = ((p: {}) => 1) as any as WithStatic<{}, 'maybe' | 'ready'>

c.maybe = ""

Playground Link

修改

关于评论,您可以允许c.maybe.ready但不允许c.maybe.maybe,但是您将需要使用联合而不是元组:

type WithStatic<P, T extends string> = {
  (props: P): void;
} & {
  [K in T]: Exclude<T, K> extends never? string : WithStatic<P, Exclude<T, K>>; // Added bottom 
}

let c = ((p: {}) => 1) as any as WithStatic<{}, 'maybe' | 'ready'>

c.maybe.maybe // error
c.ready.maybe = ""
c.maybe.ready = ""

Playground Link