gpt4 book ai didi

Typescript 无法从通用接口(interface)属性推断出正确的类型

转载 作者:搜寻专家 更新时间:2023-10-30 20:56:59 34 4
gpt4 key购买 nike

我有一些场景,我有一个使用字符串、数字和 bool 值的对象,以及一个返回正确类型值的 getter,例如像这样:

interface AllowedMapTypings {
'str': string;
'lon': number;
'str2': string;
}

const obj: AllowedMapTypings = {
'str': 'foo',
'lon': 123,
'str2': 'foo'
};

function foo<T extends keyof AllowedMapTypings>(key: T): AllowedMapTypings[T] {
return obj[key];
}

let str = foo('str'); // correctly inferred type 'string'

但是,如果我使用推断的接口(interface)类型作为参数,它就不起作用:

function fn<T extends keyof AllowedMapTypings>(key: string, kind: T, value: AllowedMapTypings[T]) {
if (kind === 'str') {
console.log(value.length); // Property 'length' does not exist on type 'AllowedMapTypings[T]'.
}
}

看起来条件 kind === 'str' 没有正确地完成它作为类型保护的工作。我是否遗漏了什么,或者这是 TS 中缺少的功能/错误?

最佳答案

这是一个已知限制,请参阅 microsoft/TypeScript#13995microsoft/TypeScript#24085 .当您执行 if (kind === 'str') {} 时发生的那种控制流缩小不会作用于泛型类型参数或此类类型的值。我认为有人会争辩说 if (kind === 'str') {} 应该将 kind 的类型从 T 缩小到 T & 'str',但即使编译器为您做了这些,它也不会缩小 value 的类型。即使您知道 kind 的类型与 value 的类型相关,编译器也不知道。

你总是可以通过自由使用 type assertions 来解决这个问题.如果您想要更多的类型安全性,您可以使用一种变通方法,将泛型类型的值扩展为具体联合,并将相关值打包到一个变量中,该变量可以按您期望的方式缩小。例如:

type KindValuePair<K extends keyof AllowedMapTypings = keyof AllowedMapTypings> =
K extends any ? [K, AllowedMapTypings[K]] : never;

类型 KindValuePair 扩展为 ["str", string] | [“lon”,数字] | ["str2", string],这是您实际希望允许的事物的联合,如 kindvalue。 (我本可以手动将 KindValuePair 设置为该联合,但我使用 distributive conditional type 让编译器帮我解决。)

然后你可以这样做:

function fn<T extends keyof AllowedMapTypings>(key: string, kind: T, value: AllowedMapTypings[T]) {
const kindValuePair = [kind, value] as KindValuePair; // assertion here
if (kindValuePair[0] === 'str') {
console.log(kindValuePair[1].length); // okay
}
}

您断言 [kind, value]KindValuePair 类型,然后您可以在 kindValuePair 上使用控制流缩小来维护在检查 'str' 后,您希望在其元素之间看到的关系。如果这对你有用,你甚至可以使用 rest parameters 使函数具体化而不是通用化。 :

function fn(key: string, ...kindValuePair: KindValuePair) {
if (kindValuePair[0] === 'str') {
console.log(kindValuePair[1].length); // okay
}
}

这完全避免了断言,并且是我所能想象的类型安全的。它还具有像这样禁止调用的副作用:

fn("", Math.random() < 0.5 ? 'str' : 'lon', 1); // error

在通用版本中是允许的(它将 T 指定为 'str' | 'lon')。

关于Typescript 无法从通用接口(interface)属性推断出正确的类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54535216/

34 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com