typescript - 部分的, 但用方法扩展
问题描述
我想创建泛型类型(带参数T
),它允许以下行为:
- if
K
是T
允许值类型的键T[K] | ((params?: Partial<T>) => string)
- 如果
K
不是 的键T
,则允许值类型((params?: Partial<T>) => string)
我试过这段代码:
type PartialOrFunction<T> = {
[K in keyof T]?: T[K] | ((params?: Partial<T>) => string)
} & {
[K: string]: ((params?: Partial<T>) => string);
};
但它拒绝Partial<T>
( TS2345
)。
然后我尝试了这段代码:
type PartialOrFunction<T> = {
[K in keyof T]?: T[K] | ((params?: Partial<T>) => string)
} & {
[K: string]: ((params?: Partial<T>) => string) | any;
};
但它不对无关属性执行类型检查(因为它允许any
)。
解决方案
如果您想要PartialOrFunction
我在评论中提到的通用约束版本,就在这里。
您PartialOrFunction<T>
是我想调用的特定情况,它采用没有索引签名DefaultProperty<T, D>
的对象类型并添加了 type 的“默认”属性。这与字符串索引签名不同,后者要求所有属性都可以分配给. 但正如您所见,TypeScript 不支持这一点。T
D
T
D
为了做到这一点,您需要能够否定keyof T
要从中排除的类型string
,并且您需要能够使用这种否定类型作为索引签名的键类型。现在你也做不到。因此,对于DefaultProperty<T, D>
您想说的话:
// this is not valid TypeScript (yet?)
type DefaultProperty<T, D> = T & { [k: string & not keyof T]: D }
但你不能。你能得到的最接近的是
type MakeshiftDefaultProperty<T, D> = T & {[k: string]: D | T[keyof T]}
但这太宽泛了,因为它允许在您的额外属性中出现不需要的东西。如果你能处理好,那就太好了。否则,请继续阅读:
所以你可以做的是创建一个VerifyDefaultProperty<T, D, C>
接受候选类型C
并返回一个新类型的类型,就像DefaultProperty<T, D>
. 如果C
可分配给VerifyDefaultProperty<T, D, C>
,则C
是有效的DefaultProperty<T, D>
。如果C
不能分配给,那么VerifyDefaultProperty<T, D, C>
它们不同的地方C
就是有问题的地方。
// caveat: T should probably not already have an index signature
type VerifyDefaultProperty<T extends object, D, C> =
{ [K in keyof C]: K extends keyof T ? T[K] : D }
例如:
interface T {x: number, y: string};
type D = boolean;
interface CGood {x: 0, y: "y", z: true};
type VGood = VerifyDefaultProperty<T, D, CGood>;
// type VGood = {x: number, y: string, z: boolean}
// CGood is assignable to VGood: yay!
interface CBad {x: 0, y: "y", z: true, oops: 123};
type VBad = VerifyDefaultProperty<T, D, CBad>;
// type VBad = {x: number, y: string, z: boolean, oops: boolean}
// CBad is *not* assignable to VBad: boo!
// specifically, CBad['oops'] is not assignable to boolean
现在,由于 TypeScript 不支持DefaultProperty<T, D>
,它也不支持PartialOrFunction
. 但我们可以从中获取VerifyDefaultProperty<T, D, C>
并制作 a VerifyPartialOrFunction<T, C>
,其作用方式相同。也就是说,当且仅当是有效的 时,C
可分配给,并且任何偏差都可以在错误消息中使用:VerifyPartialOrFunction<T, C>
C
PartialOrFunction
type VerifyPartialOrFunction<T, C> = VerifyDefaultProperty<
{ [K in keyof T]?: T[K] | ((params?: Partial<T>) => string) },
((params?: Partial<T>) => string),
C
>;
最后,我们引入了一个辅助函数,它接受一个类型的参数并返回它,但如果不是有效C
的,则会引发编译时错误。请注意,此函数必须是curried,因为您希望手动传入类型并自动推断类型,但 TypeScript 从 TS3.5 开始不支持部分类型参数推断。所以我们需要把它分解成两个函数:C
VerifyPartialOrFunction<T, D, C>
T
C
const verifyPartialOrFunction = <T>() =>
<C>(x: C & VerifyPartialOrFunction<T, C>): C =>
x;
现在,让我们测试一下:
// Tests
interface Foo {
a: string,
b: number,
c: boolean
}
const goodPartialOrFunctionFoo = verifyPartialOrFunction<Foo>()({
a: (x?: Partial<Foo>) => "a",
b: 1,
w: () => "w"
}); // okay
const badPartialOrFunctionFoo = verifyPartialOrFunction<Foo>()({
a: 1, // error!
// Type 'number' is not assignable to type
// 'number & ((params?: Partial<Foo> | undefined) => string)'.
b: (x: string) => "oops", // error!
// Type '(x: string) => string' is not assignable to type
// '((x: string) => string) &
// (number) | ((params?: Partial<Foo> | undefined) => string))'.
w: "w"; // error!
// Type 'string' is not assignable to type
// 'string & (params?: Partial<Foo> | undefined) => string'.
})
这看起来像你想要的行为。
所以这很好。正如你所看到的,它拖累了很多类型系统,并且每个依赖的值/函数/类型PartialOrFunction
现在都必须处理一个额外的泛型类型参数。这可能很乏味,所以我通常只建议在用户调用以使用您的库的公开 API 上进行此类工作。在您的库中,您应该小心地使用扩展的类型,例如
type MakeshiftPartialOrFunction<T> = Partial<T> & { [k: string]:
T[keyof T] | undefined | ((params?: Partial<T>) => string)
}
当您知道某些东西是安全的但编译器不安全时 ,使用类型断言。
希望有帮助;祝你好运!
推荐阅读
- docker - 无法在远程服务器上的 Docker 容器上启动 GUI 程序
- google-cloud-platform - 在 GKE 上为 Hashicorp Vault 配置插件目录
- angular - 为什么 Angular 8 所需的验证与 ngValue 的行为不同?
- node.js - nginx 位置未正确加载
- c# - C#在鼠标移动时隐藏工具提示并在鼠标悬停时再次显示它而不离开控制
- android - 检查地图上的标记是否已被删除 - Android
- r - 为什么 dplyr 过滤器没有捕获 NA
- javascript - 从 wordpress 网页创建可下载的 PDF
- windows - pthread_cond_wait 的 Win32 等效 API
- ruby - Sprintf 浮点数行为从 Ruby 2.3 更改为 2.4