我希望TS的最新版本(参见示例#43183)能够在不使用不安全铸造的情况下实现这种模式.有什么 idea 吗?(playground)

export type Mapping = {
    number: number;
    string: string;
};

function get<K extends keyof Mapping>(key: K): Mapping[K] {
    switch (key) {
        case "number":
            // ERROR: Type 'number' is not assignable to type 'Mapping[K]'
            return 42; // Adding "as Mapping[K]" works, but that's not type-safe
        case "string":
            // ERROR: Type 'string' is not assignable to type 'Mapping[K]'
            return "hello";
        default:
            throw "Never reached, but otherwise TS throws: Function lacks...";
    }
}

const usageWorksAsExpected = { n: get("number"), s: get("string") };

推荐答案

不,在microsoft/TypeScript#43183中实现的TypeScript 4.3中添加的对contextual narrowing of generics的支持并没有达到您的预期.该支持将key的类型从K缩小到"number""string",但它不会缩小类型参数K本身的类型.即使key被确认为"number",类型参数K也保持不变,因此编译器无法验证42是否可分配给Mapping[K].这很令人沮丧.

还有一些悬而未决的问题,比如microsoft/TypeScript#33014microsoft/TypeScript#27808,它们提出了编译器缩小K范围的方法.但这些措施尚未实施.

如果您想重写get(),使其编译时不会出现错误,也不会放弃类型安全性,那么您可以这样做:

function get<K extends keyof Mapping>(key: K): Mapping[K] {
    return {
        number: 42,
        string: "hello"
    }[key] // okay
}

您的返回类型是泛型indexed access type Mapping[K].编译器can验证,如果使用K类型的键对Mapping类型的值进行索引,则会得到Mapping[K]类型的值,这就是上述重写代码工作的原因.

这依赖于获得Mapping类型的值.有时你不能或不想这样做,因为这会迫使你在不想的时候Eager 地预先计算每个可能输入的结果.如果是这样,您可以实现getters,这样Mapping对象就可以被延迟计算:

function get<K extends keyof Mapping>(key: K): Mapping[K] {
    return {
        get number() { return 42 },
        get string() { return "hello" }
    }[key]
}

Playground link to code

Typescript相关问答推荐

为什么TypScript对条件函数类型和仅具有条件返回类型的相同函数类型的处理方式不同?

类型脚本不会推断键的值类型

等待的R没有用响应对象展开到R

在Angular中,如何在文件上传后清除文件上传文本框

输入元素是类型变体的对象数组的正确方法是什么?'

隐式键入脚本键映射使用

学习Angular :无法读取未定义的属性(正在读取推送)

TypeScrip-将<;A、B、C和>之一转换为联合A|B|C

TypeScrip:逐个 case Select 退出noUncheck kedIndexedAccess

使用订阅时更新AG网格中的行

是否将自动导入样式从`~/目录/文件`改为`@/目录/文件`?

vue-tsc失败,错误引用项目可能无法禁用vue项目上的emit

如何过滤文字中的类对象联合类型?

声明遵循映射类型的接口

打字错误TS2305:模块常量没有导出的成员

设置不允许相同类型的多个参数的线性规则

有没有什么方法可以使用ProvideState()提供多个削减器作为根减减器

Typescript不允许在数组中使用联合类型

TypeScript:方法获胜';t接受较窄类型作为参数

在类型{}上找不到带有string类型参数的索引签名 - TypeScript