打字稿中的相关不相交联合

Dependent Disjoint Unions in Typescript

给定一个简单的 Record<string, Record<string, any>> 定义为可能命名配置的常量记录,我想要一个基于键动态限制配置类型的类型。 考虑以下示例:

const configs = {
    "fooConfig": {c: "s"},
    "barConfig": {c: 1, b: 2}    
} as const;

type ConfigDef<k extends keyof typeof configs = keyof typeof configs> = {
    a: k,
    v: typeof configs[k]
}

const x: ConfigDef = {
    a: "fooConfig",
    v: {c: 1, b: 2} // this should not work
}

至关重要的是,对于类型 ConfigDef,我需要用户能够隐式使用该类型,而无需将配置的实际键作为通用类型传递(即他们不需要使用显式语法 const x: ConfigDef<"fooConfig">)。这可能吗?如果可能,怎么做?

如果您想将 ConfigDef 引用为特定 (non-generic) 类型,则它的计算结果应为 union of your original ConfigDeg<K> for each K in keyof typeof configs. That is, you need to distribute ConfigDef<K> across the union keyof typeof configs. One way to do this is to write a distributive object type as coined in microsoft/TypeScript#47109:

type ConfigDef = { [K in keyof typeof configs]: {
    a: K,
    v: typeof configs[K]
} }[keyof typeof configs]

计算结果为

/* type ConfigDef = {
    a: "fooConfig";
    v: {
        readonly c: "s";
    };
} | {
    a: "barConfig";
    v: {
        readonly c: 1;
        readonly b: 2;
    };
} */

因此给你你想要的行为:

const x: ConfigDef = {
    a: "fooConfig",
    v: { c: 1, b: 2 } // error!
}

分布式对象类型是当你立即index into a mapped type。一般形式是{[K in KS]: F<K>}[KS],其中KS是keylike类型。在您的情况下,我们将 KS 设为 keyof typeof configs,将 F<K> 作为您的原始 ConfigDef<K>

Playground link to code