好
{
values: ['John', 'Steven', 'Sarah'],
oneOfValue: 'Sarah',
}
坏
{
values: ['John', 'Steven', 'Sarah'],
oneOfValue: 'Joe',
}
我一直在泛型和东西,但似乎无法使其正常工作。
假设您希望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来防止推断:
这也可行,并且错误消息是您所期望的。
好的,希望其中一种解决方案对您有用。祝你好运!
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" });