typescript - 为什么这个映射类型会移除 `?` 装饰器?我们如何在不删除它的情况下获得类似的结果?
问题描述
问题
我们正在构建一个不包括 type 属性的映射类型Function
。我们的方法有一个问题:它还?
从映射属性中删除了可选的 ( ) 装饰器。
再生产
这是该行为的简化再现。NoOpMap1
行为如我们所愿,并且NoOpMap2
有问题的行为。
type NoOpMap1<T> = { // Good. This one does not remove the ?
[K in keyof T]: T[K];
};
type Keys<T> = {
[K in keyof T]: K;
}[keyof T];
type NoOpMap2<T> = { // Problem. This one removes the ?
[K in Keys<T>]: T[K];
};
演示
type SomeType = {
foo?: string,
}
// type SomeTypeNoOpMap1 = { foo?: string; }
type SomeTypeNoOpMap1 = NoOpMap1<SomeType>;
// type SomeTypeNoOpMap2 = { foo: string; }
type SomeTypeNoOpMap2 = NoOpMap2<SomeType>;
NoOpMap1
表现如预期。?
它将装饰器保留在foo
物业上。NoOpMap2
删除它。
问题
为什么要NoOpMap2
删除?
装饰器?我们如何在不删除它的情况下获得类似的结果?
实际用例
这是我们正在尝试构建的完整类型:
type DataPropertyNames<T> = {
[K in keyof T]: T[K] extends Function ? never : K;
}[keyof T];
type DataPropertiesOnly<T> = {
[K in DataPropertyNames<T>]
: T[K] extends (string | number | boolean) ? T[K]
: T[K] extends (infer A)[] ? DataPropertiesOnly<A>[]
: DataPropertiesOnly<T[K]>;
};
如前所述,上述类型负责删除类型的属性,Function
而不?
从剩余的属性中删除装饰器。
解决方案
如果要保留映射类型中属性的可选/只读状态,则需要确保编译器将映射视为同态。我知道有两种方法可以做到这一点。
一种是映射的形式{[K in keyof T]: ...}
是您直接映射keyof T
some T
,通用或具体的形式。你必须有类似直接in keyof
出现在类型中的东西,否则它不会被计算在内。
interface Foo {
optional?: string;
readonly viewonly: string;
}
type Homomorphic = { [K in keyof Foo]: 0 };
// type Homomorphic = {
// optional?: 0 | undefined;
// readonly viewonly: 0;
// }
type KeyOf<T> = keyof T
type NonHomomorphic = { [K in KeyOf<Foo>]: 0 };
// type NonHomomorphic = {
// optional: 0;
// viewonly: 0;
// }
另一种方法是映射一个泛型类型参数,K
该类型参数已被限制为另一个泛型类型参数。所以:keyof T
T
type GenericConstraint<T, K extends keyof T> = { [P in K]: 0 };
type ConstrainedHomomorphic = GenericConstraint<Foo, keyof Foo>;
// type ConstrainedHomomorphic = {
// optional?: 0 | undefined;
// readonly viewonly: 0;
// }
type OnlySomeKeysStillHomomorphic = GenericConstraint<Foo, "viewonly">;
// type OnlySomeKeysStillHomomorphic = {
// readonly viewonly: 0;
// }
后一种方法是专门添加的,以获取部分映射类型,例如Pick<T, K>
同态。正是这种方法让您的实际用例发挥作用:
// unchanged
type DataPropertyNames<T> = {
[K in keyof T]: T[K] extends Function ? never : K;
}[keyof T];
// quick abort if T is a function or primitive
// otherwise pass to a homomorphic helper type
type DataPropertiesOnly<T> =
T extends Function ? never :
T extends object ? DPO<T, DataPropertyNames<T>> :
T
// homomorphic helper type
type DPO<T, KT extends keyof T> = {
[K in KT]
: T[K] extends (string | number | boolean) ? T[K]
: T[K] extends (infer A)[] ? DataPropertiesOnly<A>[]
: DataPropertiesOnly<T[K]>;
}
我认为这将按照您想要的方式进行。好的,希望有帮助;祝你好运!
推荐阅读
- c - switch case 中的另一种情况
- swiftui - SwiftUI 侧边栏不记得屏幕
- python - 如何在字典初始化时调用 __set_item__?
- docker - 将 Verdaccio Registry 与 Skaffold 一起使用
- c# - 如何在不编辑视图的情况下在 WPF MVVM 中生成网格的行和列?
- javascript - Javascript一次选择所有选项
- python - 检查车辆停放了多长时间
- c# - 如何在服务器端 Blazor 应用程序的新浏览器窗口或选项卡中的 Google.Protobuf.ByteString 中显示 pdf?
- javascript - 直接加载 /index.html 时出现 Vuejs/Nuxt 404 错误
- mingw - 链接 DLL 时,如何让 MinGW gcc 忽略 VERSIONINFO 中的 InternalName?