我的目标是获取一个文本对象,并将每个属性转换为独立对象的联合.

例如:

{
 200: number;
 500: string;
}

vt.进入,进入

{
 200: number;
 500?: undefined;
} |
{
 500: string;
 200?: undefined;
}

我相信我能够得到这个水平,除了任何时候我添加一个随机属性以及一个有效的属性,它不会出错.我怎么把它的错误? ie

{
 100: "why no error", // this should error error...
 200: 100,
}

完整代码示例:

type AllKeys<T> = T extends unknown ? keyof T : never;
type Id<T> = T extends infer U ? { [K in keyof U]: U[K] } : never;
type _ExclusifyUnion<T, K extends PropertyKey> = T extends unknown
  ? Id<T & Partial<Record<Exclude<K, keyof T>, never>>>
  : never;
type ExclusifyUnion<T> = _ExclusifyUnion<T, AllKeys<T>>;
type split_return_type<T> = {
  [K in keyof T]: { [P in K]: T[P] };
};
type ValueOf<T> = T[keyof T];
export type SchemezLayoutReturnType<T> = Promise<
  ExclusifyUnion<ValueOf<split_return_type<Required<T>>>>
>;

function TestFnx<T>(input: T, handleAsync: () => SchemezLayoutReturnType<T>) {
    return {
        input,
        handleAsync,
    }
}

type required_return_type = {
    200: number;
    500: string;
}
const rrr: required_return_type = {
    200: 100,
    500: "00"
};
TestFnx(rrr, async () => {
    return {
        100: "200",
        200: 100,
        // 500: "oii",
    }
});

参考文献:Playground

推荐答案

免责声明:TypeScrip中的对象类型不是密封的,因此无法真正禁止多余或意外的属性.你所能做的最好的事情就是劝阻他们.有关示例和讨论,请参见Is it possible to constrain the types of generics to only allow known properties?.


首先,我们可以简化您的类型,将{a: string, b: number}这样的对象类型转换为{a: string, b?: never} | {a?: never, b: number}这样的union个单一属性类型:

type MakeUnion<T> = {
  [K in keyof T]: Pick<T, K> & { [P in Exclude<keyof T, K>]?: never }
}[keyof T]
    

这称为"分布式对象类型"(如在ms/TS#47109中所创造的),在这种类型中,您可以创建mapped type,然后立即创建index into it以获得所有属性的并集.{[K in keyof T]: F<K>}[keyof T]相当于keyof T中的所有密钥K1K2F<K1> | F<K2> | ⋯.

那里的F<K>Pick<T, K> & {[P in Exclude<keyof T, K>]?: never}.这意味着对于K中的每个属性键,我们将生成类型Pick<T, K>(使用the Pick utility type来获取我们确实想要的一个属性),并使用{[P in Exclude<keyof T, K>]?: never}(使用the Exclude utility type)来生成intersecting,从而禁止T的所有键,即not K.从技术上讲,the impossible never type人中有optional properties人,但这几乎是禁止的.

让我们运行一个测试:

type RequiredReturnType = {
  200: number;
  500: string;
}
type UnionOfOneProps = MakeUnion<RequiredReturnType>;
/* type UnionOfOneProps = 
    (Pick<RequiredReturnType, 200> & { 500?: undefined; }) | 
    (Pick<RequiredReturnType, 500> & { 200?: undefined; }) */

这可能不会完全像{200: number, 500?: never} | {200?: never, 500: string}一样显示,但它是等同的.


接下来,您希望在呼叫testFnx()时阻止或阻止所有多余的属性.我们可以这样做:

function testFnx<T, U extends MakeUnion<T> &
  { [P in Exclude<keyof U, keyof T>]: never }>(
    input: T, handleAsync: () => Promise<U>) {
  return {
    input,
    handleAsync,
  }
}

我添加了第二个generic类型参数U.编译器仍将从input推断出T,现在它将从handleAsync‘S返回类型推断出U.然后,由于UconstrainedMakeUnion<T> & { [P in Exclude<keyof U, keyof T>]: never },它将要求handleSaync返回同时满足MakeUnion<T>(这是我们已经知道的部分)和{ [P in Exclude<keyof U, keyof T>]: never }的值.这类似于上面的键禁止类型.这里我们说,应该禁止U中的任何密钥以及T中不存在的任何密钥.同样,你不能说"禁止的",但你可以说"不可能的never型的".除非多余的属性具有类型never(这不应该发生),否则您将得到预期的错误.

让我们也来测试一下:

const rrr: RequiredReturnType = {
  200: 0,
  500: "x"
};

testFnx(rrr, async () => { return { 200: 1 } }); // okay
testFnx(rrr, async () => { return { 500: "z" } }); // okay
testFnx(rrr, async () => { return { 200: 2, 500: "y" } }); // error

testFnx(rrr, async () => { return { 700: 3, 500: "x" } }); // error
// Types of property '700' are incompatible.

看起来不错 testFnx()仍然允许你想要的单属性类型,禁止混合属性,但也禁止具有额外属性的对象.

Playground link to code

Typescript相关问答推荐

类型脚本中的Gnomeshell 扩展.如何导入.ts文件

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

如何在不使用变量的情况下静态判断运算式的类型?

如何基于对象关键字派生类型?

Angular 过滤器形式基于某些条件的数组控制

如何推断类方法未知返回类型并在属性中使用它

处理API响应中的日期对象

如何表示多层深度的泛型类型数组?

如何将定义模型(在Vue 3.4中)用于输入以外的其他用途

为什么Typescript不能推断类型?

如何在Typescript 中组合unions ?

基于泛型的条件接口键

声明遵循映射类型的接口

如何静态键入返回数组1到n的函数

Cypress-验证别名的存在或将别名中的文本与';if';语句中的字符串进行比较

两个名称不同的相同打字界面-如何使其干燥/避免重复?

如何键入';v型';

如何将元素推入对象中的类型化数组

如何强制对象数组中的对象属性具有非空字符串值?

Next.JS-13(应用程序路由)中发现无效的中间件