typescript - 如何根据属性是否存在于另一种类型中有条件地选择类型?
问题描述
首先,我想要以下结果:
type Wrapper<ID extends string> = { id: ID };
type WrapperWithPayload<ID extends string, Payload> = { id: ID, payload: Payload };
enum IDs {
FOO = "ID Foo",
BAR = "ID Bar",
BAZ = "ID Baz"
}
interface AssociatedTypes {
[IDs.FOO]: number;
[IDs.BAR]: number[];
}
type Result = MagicMapper<IDs, AssociatedTypes>
/*
* Result should have this type:
* {
* [IDs.FOO]: WrapperWithPayload<IDs.FOO, number>;
* [IDs.BAR]: WrapperWithPayload<IDs.BAR, number[]>;
* [IDs.BAZ]: Wrapper<IDs.BAZ>;
* }
*/
换句话说,我想提供一些字符串和一个映射,它将这些字符串的一个子集映射到另一种类型。然后,我想使用字符串作为键创建一个新类型。对于每个字符串/键,如果存在映射,我想使用类型 A,否则我想使用类型 B。
现在,我的方法是这样的:
type MagicMapper<T extends string, Mapping extends { [key in T]?: any }> = {
[key in T]: Mapping[key] extends never ? Wrapper<key> : WrapperWithPayload<key, Mapping[key]>;
};
那一个几乎达到了目标:
type Result = {
"ID Foo": WrapperWithPayload<IDs.FOO, number>;
"ID Bar": WrapperWithPayload<IDs.BAR, number[]>;
"ID Baz": Wrapper<IDs.BAZ> | WrapperWithPayload<IDs.BAZ, any>;
}
Baz 键上的联合是错误的。我认为错误在extends never
条件之内,但是用例如 undefined 替换它只会让事情变得更糟:
type MagicMapper<T extends string, Mapping extends { [key in T]?: any }> = {
[key in T]: Mapping[key] extends undefined ? Wrapper<key> : WrapperWithPayload<key, Mapping[key]>;
};
type Result = {
"ID Foo": Wrapper<IDs.FOO>;
"ID Bar": Wrapper<IDs.BAR>;
"ID Baz": Wrapper<IDs.BAZ>;
}
有什么方法可以让事情按我需要的方式工作吗?
解决方案
您可以更改测试是否存在密钥,Mapping
一切Mapping extends { [P in key]: infer U }
都会按预期工作:
type Wrapper<ID extends string> = { id: ID };
type WrapperWithPayload<ID extends string, Payload> = { id: ID, payload: Payload };
enum IDs {
FOO = "ID Foo",
BAR = "ID Bar",
BAZ = "ID Baz"
}
interface AssociatedTypes {
[IDs.FOO]: number;
[IDs.BAR]: number[];
}
type MagicMapper<T extends string, Mapping extends { [key in T]?: any }> = {
[key in T]: Mapping extends { [P in key]: infer U } ? WrapperWithPayload<key, U>: Wrapper<key> ;
};
type Result = MagicMapper<IDs, AssociatedTypes>
// same as :
type Result = {
"ID Foo": WrapperWithPayload<IDs.FOO, number>;
"ID Bar": WrapperWithPayload<IDs.BAR, number[]>;
"ID Baz": Wrapper<IDs.BAZ>;
}
推荐阅读
- php - 在这种情况下我应该使用左连接吗?
- redirect - CakePHP 4 身份验证插件 RequestAuthorizationMiddleware 重定向 url 丢失
- typescript - 打字稿界面...错误地扩展了界面
- excel - 使用 VBA 为多个条件创建新工作簿和工作表
- c# - 解释视图组件 ASP Razor MVC 生成的数据
- r - 如何获取列之间数据之间的差异?
- julia - 如何在 Julia 0.5 中获取 Pkg?(“错误:未知包 Pkg”)
- java - 实例化数组列表中的对象
- tfs - 可以以编程方式创建 Team Foundation Server 项目吗?
- c++ - 从文件中读取,并在函数 C++ 中写入数组