英文:
How to correctly infer the type of a key with direct key access and generic interface in TypeScript?
问题
我明白你在尝试解决的问题。在你提供的代码中,当尝试调用selected.callback(selected.firstMapper())
时出现了一个类型错误。你认为 TypeScript 在处理 configKey
为 "b"
的情况时,无法正确地推断类型。
你尝试了一些方法,包括创建一个更通用的接口,但并未取得成功。
你在这个小型 Playground中复现了这个问题。
请问你希望在这方面获得什么样的帮助?
英文:
I am trying to get TypeScript to correctly infer the types that are valid in the following context:
I have 2 interfaces
interface A {
first: string;
}
type B = {
second: string;
}
as well as a generic interface that has 2 mappers where the types depend on each other, something like
interface Gen<T extends string> {
firstMapper: () => T;
callback(arg: T): string;
}
I want also to have an interface that records multiple Gen
interfaces
interface Aobject {
"a": Gen<"a">,
"b": Gen<"b">,
}
Finally, I want to create function that takes a key of AObject
, retrieves the configuration from the AObject
interface calls the firstMapper
and then the callback
such as:
const buildMapping = (configKey: keyof Aobject, arg: Aobject) => {
const myA: Aobject = {
"a": {
firstMapper: () => "a",
callback: (input) => ("result" + input)
},
"b": {
firstMapper: () => "b",
callback: (input) => ("result" + input)
}
}
const selected = myA[configKey];
selected.callback(selected.firstMapper());
}
However, when I try to do this, I have an error
Argument of type 'string' is not assignable to parameter of type 'never'.
Type 'string' is not assignable to type 'never'.
However, if I change to this
if (configKey === "a") {
const selected = myA[configKey]
selected.callback(selected.firstMapper())
}
if (configKey === "b") {
const selected = myA[configKey]
selected.callback(selected.firstMapper())
}
there is no error.
My understanding is that Typescript does not understand that if the configKey
is of type "b"
, then selected
can only have the type
b {
firstMapper: () => "b",
callback: (input) => ("result" + input)
}
instead TS "thinks" that the callback can be called with both and tries to create the intersection of type "a"
and type "b"
which results in never
.
I am wondering if there is a better way to handle this use case, because I find it weird having to write an if
case executing the same piece of code.
I tried to create a more generic interface because I thought the problem might come from the AObject
definition but no success.
I created a small playground to reproduce the issue.
答案1
得分: 0
以下是翻译好的内容:
-
满足编译器的两种方法:
-
您可以使用 类型断言:
type Gen<T extends string> = { firstMapper: () => T; callback(arg: T): string; }; type Aobject = { a: Gen<"a">; b: Gen<"b">; }; function buildMapping<K extends keyof Aobject>( configKey: K, arg: Aobject, ) { const selected = arg[configKey] as Gen<K>; // ^^^^^^^^^ selected.callback(selected.firstMapper()); }
-
-
您可以将类型
Aobject
定义为映射类型,编译器将正确推断,无需断言。生成的类型相同:type Gen<T extends string> = { firstMapper: () => T; callback(arg: T): string; }; type Aobject = { [Str in "a" | "b"]: Gen<Str> }; //^? type Aobject = { a: Gen<"a">; b: Gen<"b">; } function buildMapping<K extends keyof Aobject>( configKey: K, arg: Aobject, ) { const selected = arg[configKey]; selected.callback(selected.firstMapper()); }
英文:
Here are two ways to satisfy the compiler:
-
You can use a type assertion:
type Gen<T extends string> = { firstMapper: () => T; callback(arg: T): string; }; type Aobject = { a: Gen<"a">; b: Gen<"b">; }; function buildMapping<K extends keyof Aobject>( configKey: K, arg: Aobject, ) { const selected = arg[configKey] as Gen<K>; // ^^^^^^^^^ selected.callback(selected.firstMapper()); }
-
You can define the type
Aobject
as a mapped type and the compiler will infer correctly without the assertion. The resulting type is the same:type Gen<T extends string> = { firstMapper: () => T; callback(arg: T): string; }; type Aobject = { [Str in "a" | "b"]: Gen<Str> }; //^? type Aobject = { a: Gen<"a">; b: Gen<"b">; } function buildMapping<K extends keyof Aobject>( configKey: K, arg: Aobject, ) { const selected = arg[configKey]; selected.callback(selected.firstMapper()); }
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论