在下面的例子中,TypeScript能够在调用getObjPath
函数时正确地推断参数,但在内部,它在satisfies ObjPaths
上失败了。有人能解释一下吗?或者给我指一个文档,我可以理解发生了什么?
const obj = {
a: '1',
b: '2',
} as const;
type Obj = typeof obj;
type ObjKeys = keyof Obj;
type ObjPaths = { [K in ObjKeys]: `${K} | ${Obj[K]}`}[ObjKeys];
const getObjPath = <K extends ObjKeys, V extends Obj[K]>(k: K, v: V) => {
return `${k} | ${v}` satisfies ObjPaths; // unable to correctly infer
}
getObjPath('a', '2'); // correctly infers
2条答案
按热度按时间o2rvlv0m1#
这里的一般问题是,TypeScript并没有一个很好的方法来表示 * 相关的联合类型 * 或限制到这种联合的generics。如果你有多个依赖于同一个联合类型值的值或表达式,那么编译器会错误地认为该表达式的每个外观都是独立的。特别是对于template literal types,这在microsoft/TypeScript#49132中讨论过。但是在X1 E3 F1 X中详细描述了一般问题。
例如,让我们看看
ObjKeys
类型的值会发生什么:在这里,
p
的类型被编译器推断为"a | 1" | "a | 2" | "b | 1" | "b | 2"
,就好像k
和obj[k]
的类型彼此独立一样。p
实际上不可能是"a | 2"
或"b | 1"
,但编译器认为它可能是,如果你试图将它赋值给ObjPaths
类型的变量,你会遇到麻烦。你希望编译器做的事情更像这样:
其中依次考虑
k
的每种可能性,然后将结果合并在一起。当您手动执行冗余操作时,这是可行的,但当您将其写入一行时则不行。要求编译器像**一样单独执行每种情况会很好,如decline microsoft/TypeScript#25051中所述,但这是不可能的。这就是这里的局限性。相关的联合处理得不是很好。幸运的是,在microsoft/TypeScript#47109中描述了一个推荐的修复,在那里我们重构成一个 * 分布式对象类型 * 和泛型。你的代码实际上非常接近;我们需要做的就是在特定的键中使
ObjPaths
成为泛型,并使函数也返回一个泛型:(Note我删除了额外的类型参数
V
除非你实际上有V
的不同可能性,否则你可能不想让函数变得比它需要的更通用而使事情复杂化。)事实上,你可以将返回类型注解为ObjPaths<K>
,而不会丢失任何你关心的信息:这仍然表现为调用方所需的行为:
Playground链接到代码
u59ebvdq2#
但你写的不是预期的类型。
试试这个: