TS条件类型,仅从联合中扩展一项

时间:2019-09-23 12:00:15

标签: typescript

我希望创建一个函数,该函数接受来自接口Foo的密钥作为输入,仅限于Foo是可接受的赋值的string的密钥

interface Foo {
  a: string;
  b?: string;
  c?: string | boolean;
}

test("a");
test("b"); // Argument of type '"b"' is not assignable to parameter of type 'never'.ts(2345)
test("c"); // Argument of type '"c"' is not assignable to parameter of type 'never'.ts(2345)

function test<K extends keyof Foo>(key: Foo[K] extends string ? K : never) {}

以上(启用严格模式)在bc上失败-有没有办法使用extends string(或类似的方法) 不必完全匹配属性的类型 ?我对其他潜在类型不感兴趣,仅是类型可以string

1 个答案:

答案 0 :(得分:0)

因此,您显然要寻找的测试是string extends Foo[K]而不是Foo[K] extends string

function test<K extends keyof Foo>(key: string extends Foo[K] ? K : never) {}

test("a"); // okay
test("b"); // okay
test("c"); // okay

如果您在考虑可分配性,T extends U意味着可以将类型T的值分配给类型U的变量。如果将“ Foo是可接受的分配的string的属性”转换为“可以将string类型的值分配给由键{索引的Foo的属性{1}}”,您得到K。在这种情况下,如果您拥有类型string extends Foo[K]的变量foo,类型Foo的变量k和类型{{1}的变量K },您应该可以编写s并进行编译。

如果您正在寻找string的属性,可以将其分配给{em> 类型为foo[k] = s的变量,则为Foo[K] extends string,相反:您可以写Foo


这也正确拒绝了非string属性:

s = foo[k]

它拒绝类型比string更窄的类型:

interface Foo {
  d: number;
}

test("d"); // error!
// Argument of type '"d"' is not assignable to parameter of type 'never'.

您无法为任何旧字符串stringinterface Foo { e: "specificString"; } test("e"); // error! // Argument of type '"e"' is not assignable to parameter of type 'never'. ,因此这就是拒绝的原因。 (如果您需要接受它,那么您将要求表达一些更复杂的内容,而且也不清楚为什么有人需要它……因为您既无法读取也无法写入foo.e = s值在这种情况下)


还请注意,s似乎不需要通用...您可以使用通用类型别名,例如:

string

,它将采用类型testtype KeysAssignableFrom<T, V> = { [K in keyof T]-?: V extends T[K] ? K : never }[keyof T]; 并返回T的键,其属性可从V进行分配。然后T变为:

V

等效于:

test()

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

Link to code