这里是代码的例子.

enum Keys {
    Key1 = 'key1',
    Key2 = 'key2',
}

type A = {
    [Keys.Key1]?: {
        value: string;
    };
    [Keys.Key2]?: {
        value: number;
    };
};

type B = {
    [Keys.Key1]?: string;
    [Keys.Key2]?: number;
};

function func(obj: A): B {
    return Object.values(Keys).reduce(
        (prev, key) => {
            if (key in obj) {
                prev[key] = obj[key].value;
            }
            return prev;
        },
        <B>{},
    );
}

prev[key] = obj[key].value;行显示错误

Type 'string | number' is not assignable to type 'undefined'.
  Type 'string' is not assignable to type 'undefined'.ts(2322)

我可以这样做:

prev[key] = (obj[key] as any).value;

或者通过代码定义来缩小类型:

function func(obj: A): B {
    return Object.values(Keys).reduce((prev, key) => {
        if (key in obj) {
            switch (key) {
                case Keys.Key1:
                    prev[key] = obj[key]?.value;
                    break;
                case Keys.Key2:
                    prev[key] = obj[key]?.value;
                    break;
            }
        }
        return prev;
    }, {} as B);
}

有没有方法在不改变代码的情况下通过类型编写安全的代码?因为可以有复杂的类型,比如B中的属性数,而A中的属性数|null,我想看到我不能将对象A转换为对象B.有任何它不会工作.

推荐答案

您遇到的问题是TypeScript不直接支持correlated union types,如microsoft/TypeScript#30581所述.它不能看你的reduce()回调,然后说prev[key] = obj[key].value对任意的key有意义. key的类型是union typeKeys.TypeScript观察的是key中的type,而不是它的identity,所以它将代码视为你写的prev[key1] = obj[key2].value,其中key1key2都是类型Keys.这显然是不安全的(因为key1 === key2不保证).事实上,这不能发生,编译器逃脱了,因为为了理解它,它需要理解某种更高阶的类型关系,形式为"无论key是什么,prev[key1]的类型和obj[key2].value的类型将是相同的".它不能自己做这样的观察;你需要 bootstrap 它.

支持的方法是重构类型,如microsoft/TypeScript#47109所述.您不直接使用联合类型,而是使用generics.所以key将是constrainedKeys的泛型类型,而不是Keys本身. 所有的操作都需要根据一个"基"对象类型,根据该基类型的泛型indexes into,以及根据该类型的泛型索引到mapped types.

您的B类型已经是相关的基类型,因为它是从Keys中的每个键到正在发生的事情的简单键相关部分的直接映射:

type B = {
    [Keys.Key1]?: string;
    [Keys.Key2]?: number;
};

您的A类型需要被重构为超过B的映射类型.否则,不能保证AB在一般情况下相关.因此,新版本的A可以是:

type A = {
    [K in keyof B]: { value: Exclude<B[K], undefined> }
}

然后,在你的reduce()回调中,你需要使key成为泛型类型,K extends Keys,而不是仅仅Keys.这意味着整个回调现在是泛型的:

function func(obj: A): B {
    return Object.values(Keys).reduce(<K extends Keys>
        (prev: B, key: K) => {
        if (key in obj) {
            prev[key] = obj[key].value;
        }
        return prev;
    },
        {},
    );
}

现在它编译.编译器认为obj[key].value是类型 Exclude<B[K], undefined>,它可以分配给B[K],即prev[key]的类型.

Playground link to code

Typescript相关问答推荐

类型脚本如何将嵌套的通用类型展开为父通用类型

TS 2339:属性切片不存在于类型DeliverableSignal产品[]上>'

如何使用axios在前端显示错误体

typescript抱怨值可以是未定义的,尽管类型没有定义

在TypeScript中使用泛型的灵活返回值类型

异步动态生成Playwright测试,显示未找到测试

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

TypeScrip 5.3和5.4-对`Readonly<;[...number[],字符串]>;`的测试版处理:有什么变化?

在类型脚本中的泛型类上扩展的可选参数

react 路由Use RouteLoaderData类型脚本错误

将带有样式的Reaction组件导入到Pages文件夹时不起作用

如何从上传文件中删除预览文件图标?

TypeScrip:使用Cypress测试包含插槽的Vue3组件

如何在TypeScript中描述和实现包含特定属性的函数?

我如何键入它,以便具有字符串或数字构造函数的数组可以作为字符串或数字键入s或n

递归JSON类型脚本不接受 node 值中的变量

如何使用 Angular 确定给定值是否与应用程序中特定单选按钮的值匹配?

可选通用映射器函数出错

在Nestjs中,向模块提供抽象类无法正常工作

React 中如何比较两个对象数组并获取不同的值?