【问题标题】:Typescript multi generics object declaration打字稿多泛型对象声明
【发布时间】:2020-01-08 13:30:16
【问题描述】:

我正在努力输入具有这种行为的函数: 给定一个对象conf,其键数未定义,每个键是一个具有valuetype 属性的对象,该函数应返回一个具有相同属性的对象,并且只有valuevalue 相同。

所以要清楚,这是一个函数输入的例子:

{
  foo: {value: 1, type: 'number'},
  bar: {value: 'hello', type: 'string'}
}

以及相应的输出:

{
  foo: 1,
  bar: 'hello'
}

这是我目前所写的:

type TypeWithName<T extends string> = 
  T extends 'number' ? number :
  T extends 'boolean' ? boolean : 
  T extends 'undefined' ? undefined :
  T extends 'array' ? string[] :
  string

declare function f<T extends string>(conf: {
  [key: string]: { default: TypeWithName<T>; type: T }
}): { [P in keyof typeof conf]: TypeWithName<T> }

这显然是不正确的,因为:

  1. T 一次只能采用一种类型(上面的示例将在属性bar 上抛出错误)
  2. 返回类型具有未定义数量的键,而不是输入对象中存在的全部且唯一的键。

但我有点迷路了,不知道该去哪里看,也不知道这是否可能。

【问题讨论】:

  • 您是否需要f() 来关心其valuetype 属性之间具有正确关系的输入值属性?如果是这样,type 可以是什么字符串?它看起来几乎类似于typeof value 的运行时输出,只是没有"array" 原语(运行时为object)。

标签: typescript generics types typescript-typings typescript-generics


【解决方案1】:

我的方法是使用从type 属性字符串到value 类型而不是条件类型的映射接口;您可能可以在此处使用条件类型,但在类型系统中使用接口更容易:

interface TypeMap {
  number: number;
  boolean: boolean;
  undefined: undefined;
  string: string;
  object: object | null;
}

我假设您希望 "string" 映射到 string,我添加了 "object"。如果你想让 "array" 映射到 string[] 你可以这样做,我猜。

然后我想使用TypeMap 来创建一个名为Config 的联合类型,表示valuetype 之间的约束,如下所示:

type Config = {
  [K in keyof TypeMap]: { value: TypeMap[K]; type: K }
}[keyof TypeMap];

如果你检查它,它相当于:

type Config = {
    value: number;
    type: "number";
} | {
    value: boolean;
    type: "boolean";
} | {
    value: undefined;
    type: "undefined";
} | {
    value: string;
    type: "string";
} | {
    value: object | null;
    type: "object";
}

现在f 函数在T 中是通用的,这是一种对象类型,其键是任意的,其属性是Config,由约束Record&lt;keyof T, Config&gt; 表示。输入类型为T,输出类型为mapsT中的每个属性为其value属性:

declare function f<T extends Record<keyof T, Config>>(
  t: T
): { [K in keyof T]: T[K]["value"] };

让我们看看它是否有效:

const output = f({
  foo: { value: 1, type: "number" },
  bar: { value: "hello", type: "string" }
}); 
// const output: { foo: number;  bar: string; }

我认为这是您期望的输出类型,对吧?如果您需要更窄的类型(例如,footype 1 而不是 number),您可以在输入中使用 const assertion

const outputNarrower = f({
  foo: { value: 1, type: "number" },
  bar: { value: "hello", type: "string" }
} as const); 
// const outputNarrower: { foo: 1; bar: "hello"; }

最后,您可以看到f() 的输入受到限制,因此您不能给出不匹配的value/type 对:

f({
  chicken: { value: 123, type: "string" }, // error!
  cow: { value: "abc", type: "string" }
});

好的,希望对您有所帮助;祝你好运!

Link to code

【讨论】:

  • 好的,很聪明,很详细,非常感谢!多亏了你,我学到了一些东西。 value 在我的真实案例应用程序中是可选的,但我可以从那里开始工作并最终找到完整的解决方案。再次感谢楼主!
【解决方案2】:

您可以尝试以下方法:

const conf = {
  foo: { value: 1, type: "number" },
  bar: { value: "hello", type: "string" }
};

type Input = {
  [key: string]: { value: any; type: string };
};

type Output<U extends Input> = { [Key in keyof U]: U[Key]["value"] };

function f<T extends Input>(input: T): Output<T> {
  // Details...
}

const returned = f(conf);

returned.foo // number
returned.bar // string

TypeScript playground

【讨论】:

  • 这是一个不错的解决方案,但它将Output[key] 的类型限制为Input[key][value] 的类型。我要做的是在我的 TypeWithName 类型的帮助下将 Output[key] 的类型限制为 Input[key][type]
  • 啊,明白了。我误解了。如果可以推断,您是否需要type 密钥?
  • 是的,我在这里简化了问题以提出问题,但在我的情况下,该值将是可选的并且仅用作默认值。
【解决方案3】:

我认为这应该足够了..... key is using 'any'

function change(x:any) {
  let y:any={};
  for(let c in x){
    console.log(c)
    y[c] = x[c].value;
  }
  return y;
}

let x={
  'foo': {value: 1, type: 'number'},
  'bar': {value: 'hello', type: 'string'}
};

console.log(change(x));

【讨论】:

    猜你喜欢
    • 2021-01-20
    • 1970-01-01
    • 2018-11-06
    • 1970-01-01
    • 1970-01-01
    • 2019-01-16
    • 2020-10-11
    • 1970-01-01
    • 2022-01-26
    相关资源
    最近更新 更多