如何使对象属性键入为TypeScript中另一个属性的字符串数组中的字符串

问题描述 投票:0回答:1

{
  values: ['John', 'Steven', 'Sarah'],
  oneOfValue: 'Sarah',
}

{
  values: ['John', 'Steven', 'Sarah'],
  oneOfValue: 'Joe',
}

我一直在泛型和东西,但似乎无法使其正常工作。

typescript
1个回答
0
投票

假设您希望values的字符串元素是动态的,那么您就不能在TypeScript中将这种类型表示为具体的非泛型类型。相反,您将需要类似于以下内容的通用类型:

type Acceptable<K extends string> = { values: K[], oneOfValue: K };

然后您可能要根据所写的值推断K。不幸的是,这并不能使您到达那里:

const inferAcceptable = <K extends string>(a: Acceptable<K>) => a;

inferAcceptable({
  values: ['John', 'Steven', 'Sarah'],
  oneOfValue: 'Joe',
}); // no error! K inferred as "John" | "Steven" | "Sarah" | "Joe"

这是因为将从K属性中推断出Acceptable<K>中的通用values,从oneOfValue属性中推断出[[和。您最终得到"John" | "Steven" | "Sarah" and "Joe"的并集,这通常是人们希望编译器执行的操作,但未能抓住这一点。


您真正想要的是某种告诉inferAcceptable()函数从K属性推断出类型参数values

not

oneOfValue推断出来的方法。这个想法称为“非推断类型参数用法”,并且存在一个开放功能请求(请参见microsoft/TypeScript#14829)。没有“官方”方法来执行此操作,但是GitHub问题提供了许多在各种情况下都可以使用的技术和变通方法。One such technique可以接受您不想用于推理的任何类型参数T,并像intersect一样使用empty object type来获取它。因此,让我们在这里尝试:

(T & {})

它起作用吗?

const inferAcceptable = <K extends string>(v: { values: K[], oneOfValue: K & {} }) => v;

是;接受const good = inferAcceptable({
  values: ['John', 'Steven', 'Sarah'],
  oneOfValue: 'Sarah',
}); // okay

const bad = inferAcceptable({
  values: ['John', 'Steven', 'Sarah'],
  oneOfValue: 'Joe', // error!
  //~~~~~~~~ <-- "Joe" is not ("John" & {}) | ("Steven" & {}) | ("Sarah" & {})
});
,并且拒绝good。但是,bad中的错误消息带有那些讨厌的交叉点。 

[bad使用延迟的Another technique来防止推断:

conditional types

这也可行,并且错误消息是您所期望的。


好的,希望其中一种解决方案对您有用。祝你好运!

type NoInfer<T> = [T][T extends any ? 0 : never]; const inferAcceptable = <K extends string>(v: { values: K[], oneOfValue: NoInfer<K> }) => v; const good = inferAcceptable({ values: ['John', 'Steven', 'Sarah'], oneOfValue: 'Sarah', }); // okay const bad = inferAcceptable({ values: ['John', 'Steven', 'Sarah'], oneOfValue: 'Joe', // error! //~~~~~~~~ <-- "Joe" is not "John" | "Steven" | "Sarah" });

© www.soinside.com 2019 - 2024. All rights reserved.