两个等价的泛型类型在函数体中被区别对待



我有一个接受类型T作为泛型参数的类型。

在泛型函数的类型参数中,我使用Pick从对象的键派生T

如果我使用一个中间类型变量来获取这些键,它就不能在函数体中充当T。如果我跳过中间变量(其他的都一样(,它的行为就像T!

操场上注释良好的例子(这是我能做的最短的(

转载如下:

//util to inspect types
type Id<T> = T extends object ? {} & { [P in keyof T]: Id<T[P]> } : T;
//-----
type AllKeys = "goodkey1" | "goodkey2" | "badkey";
type GoodKeys = "goodkey1" | "goodkey2"; //no badkey
//generic type only accepts good keys
type GenericType<T extends GoodKeys> = {
something: T
};
//an object type with ALL keys (good and bad)
type AnObjectWithAllKeys = { [key in AllKeys]: any };
export function genericFunction<
AllKeysObj extends AnObjectWithAllKeys,
//an object with only good keys:
PermittedObj extends Pick<AllKeysObj, GoodKeys>,
//should only be good keys but errors as a type argument in function body
InferredPermittedKeysError extends keyof PermittedObj,
//exactly same as above only expanded (just no intermediate type)
InferredPermittedKeysNOError extends keyof Pick<AllKeysObj, GoodKeys>,
>(
) {
//why is this one giving an error string | number | symbol' is not assignable to type 'GoodKeys'
type Err = GenericType<InferredPermittedKeysError>;
//but the equivalent one does not??
type NoErr = GenericType<InferredPermittedKeysNOError>;
//return types to inspect
return null as unknown as [InferredPermittedKeysError, InferredPermittedKeysNOError];
}
type Ret = ReturnType<typeof genericFunction>;
//hover over types and they are the same!
type ErrType = Id<Ret[0]>;
type NoErrType = Id<Ret[1]>;

这是怎么回事?

类型InferredPermittedKeysError被定义为keyof PermittedObj的一个子类型,而PermittedObjPick<AllKeysObj, GoodKeys>的任何子类型,这意味着它除了GoodKeys之外还可以具有任意属性。所以InferredPermittedKeysError可以是任意的属性名称,它不必是GoodKeys的子类型。

例如:

  • 假设AllKeysObj恰好等于AnObjectWithAllKeys
  • 还假设PermittedObj是类型{goodkey1: string, goodkey2: string, foobar: string},它实际上是Pick<AllKeysObj, GoodKeys>的一个子型
  • 则CCD_ 16将是并集类型CCD_
  • 然后假设InferredPermittedKeysError是类型'foobar',这是允许的,因为它是该并集类型的子类型
  • 当然,'foobar'并没有扩展GoodKeys

相反,InferredPermittedKeysNOError必须确实是GoodKeys的一个子型,因为它被定义为keyof Pick<AllKeysObj, GoodKeys>的一个子类型,等于类型GoodKeys。所以事实上,你的两个泛型类型是而不是等价的;您的Id函数在这里给出了错误的结果。

为了确认,允许以下调用,没有类型错误:

// const test: ['foobar', 'goodkey1']
const test = genericFunction<
AnObjectWithAllKeys,
{goodkey1: string, goodkey2: string, foobar: string},
'foobar',
'goodkey1'
>();

游乐场链接

最新更新