英文:
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
Aobjectas 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()); }
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论