我想创建泛型(使用参数T
),该类型具有以下行为:
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
)。
答案 0 :(得分:1)
如果要使用我在评论中提到的PartialOrFunction
的通用约束版本,请在此处。
您的PartialOrFunction<T>
是我想调用DefaultProperty<T, D>
的特定情况,它采用没有索引签名的对象类型T
并添加了{类型的“默认”属性{1}}。这与字符串索引签名不同,后者要求D
中的所有属性都可分配给T
。但是正如您所看到的,TypeScript不支持该功能。
为此,您需要negate types才能将D
从keyof T
中排除,并且需要使用诸如{ {3}}。目前您无法执行任何操作。因此,对于string
,您想要说类似:
DefaultProperty<T, D>
the key type of an index signature。可能最接近的是
// 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>
并从中获得一个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
的参数并返回它,但是如果C
不是有效的VerifyPartialOrFunction<T, D, C>
则抛出编译时错误。请注意,此函数必须为but you can't,因为您希望手动传递类型T
并自动推断类型C
,但是TypeScript不支持curried作为TS3.5。因此,我们需要将其分为两个功能:
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)
}
并在知道安全的情况下使用partial type parameter inference,但编译器则不然。
希望有所帮助;祝你好运!