依赖于 TypeScript 的字符串文字属性和索引

标签 typescript

类似于Typescript: Type of a property dependent on another property within the same object我想要一种属性依赖的类型。

const attributes = {
    physical: {
        traits: {
            strength: 1,
            dexterity: 1,
            stamina: 1,
        }
    },
    social: {
        traits: {
            charisma: 1,
            manipulation: 1,
            appearance: 1,
        }
    },
    mental: {
        traits: {
            perception: 1,
            intelligence: 1,
            wits: 1,
        }
    }
};

type AttributeTrait =
    | {
        category: 'physical';
        trait: keyof typeof attributes.physical.traits;
    }
    | {
        category: 'social';
        trait: keyof typeof attributes.social.traits;
    }
    | {
        category: 'mental';
        trait: keyof typeof attributes.mental.traits;
    };

const action: AttributeTrait = {
    category: 'social',
    trait: 'manipulation'
}

function increment(action: AttributeTrait) {
    attributes[action.category].traits[action.trait]++; // error 7053
}

在函数中,action.trait 的类型为:

(property) trait: "strength" | "dexterity" | "stamina" | "charisma" | "manipulation" | "appearance" | "perception" | "intelligence" | "wits"

因此它不能用于索引traits

我该如何解决这个问题?

最佳答案

我没有适合您的非冗余且类型安全的解决方案。你的 AttributeTrait 是我一直称之为 correlated record type 的东西.这是一个有区别的联合体,其中一些代码对于联合体的每个成员都是安全的,但是编译器看不到它对于整个联合体是安全的,因为它忘记了 相关性 categorytrait 属性。

如果您编写冗余代码,错误就会消失:

function incrementRedundant(action: AttributeTrait) {
    switch (action.category) {
        case "physical":
            attributes[action.category].traits[action.trait]++;
            return;
        case "social":
            attributes[action.category].traits[action.trait]++;
            return;
        case "mental":
            attributes[action.category].traits[action.trait]++;
            return;
    }
}

但是尽您所能,您不能将这些案例折叠成一行代码并让编译器为您验证安全性。这就是我提交 microsoft/TypeScript#30581 的原因,以及我提交 microsoft/TypeScript#25051 的原因之一.由于您不能要求编译器将单行 视为好像 它是在 switch/case 语句中写出的,最好的我能想到的是使用 type assertion告诉编译器你比它更了解。

一种方法是撒一点谎,告诉编译器 attributes 实际上拥有所有 category 对象的所有 trait :

function increment(action: AttributeTrait) {
    (attributes as
        Record<AttributeTrait["category"], {
            traits: Record<AttributeTrait["trait"], number>
        }>
    )[action.category].traits[action.trait]++;
}

这比冗余代码的类型安全性差,但至少它可以让您继续前进。


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

Playground link to code

关于依赖于 TypeScript 的字符串文字属性和索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65369273/

相关文章:

javascript - 我可以在 JS 项目中使用 TS 库吗?

typescript - 在 Typescript 中修改数组类型

javascript - TypeScript tsconfig 某些文件夹中的输出文件

javascript - React + TypeScript : Scroll event type not assignable to window. addEventListener

typescript - 如何避免在 Angular 2 中导入相对路径很长的内容?

angular - 需要一个安全的 HTML,得到一个 Style

javascript - 通过增加 javascript 中的数字使记录唯一

angular - 扩展 BaseRequestOptions 时未定义注入(inject)的依赖项

TypeScript:命名空间与类

asynchronous - TypeScript/Angular 2 - 在另一个完成后调用一个函数