如何在不使用 Typescript 进行转换的情况下从 Partial<T> 移至 T

jst*_*lne 4 typescript

一般打字稿问题说我迭代一个我知道其内容的数组,并应用一个归约来获取一个我确实知道类型的对象,例如:

interface IMyInterface {
  a: number;
  b: number;
  c: number;
}
const result: IMyInterface = ['a','b','c'].reduce((acc: Partial<IMyInterface>,val)=>({...acc,[val]: 1}), {});
Run Code Online (Sandbox Code Playgroud)

现在这不起作用,因为结果预计是Partial<IMyInterface>有意义的,考虑到 TS 无法告诉数组的内容将产生“完整”对象。但是,我需要做什么才能使结果为 IMyInterface 类型而不需要as IMyInterface

这是一个 repl https://repl.it/@Sudakatux/KaleidooscopyGraciousApplicationpackage

提前致谢

jca*_*alz 6

这里的简短答案是:您非常需要使用类型断言,因为编译器不可能知道您正在做的事情是安全的。


更长的答案:为了让编译器知道发生了什么,您需要回调是通用的。这是一种输入方法:

const cb = <K extends keyof IMyInterface, T extends Partial<IMyInterface>>(
    acc: T, val: K): T & Record<K, number> => ({ ...acc, [val]: 1 })
Run Code Online (Sandbox Code Playgroud)

该类型签名表明 需要cb两个参数accval。该acc参数是T必须可分配给 的泛型类型Partial<IMyInterface>,并且该val参数是K必须可分配给 的泛型类型keyof IMyInterface。那么回调的输出是T & Record<K, number>:也就是说,它是一个具有来自 的所有键和值的对象T,但它number在键 处也有一个确定的值K。因此,当您调用 时cb(),返回值的类型可能与 的类型不同acc

这为编译器提供了足够的信息,使您可以避免类型断言...但前提是您手动执行类似reduce()操作cb(),通过将循环展开为一堆嵌套调用:

const result: IMyInterface = cb(cb(cb({}, "a"), "b"), "c"); // okay
const stillOkay: IMyInterface = cb(cb(cb({}, "a"), "c"), "b"); // okay
const mistake: IMyInterface = cb(cb(cb({}, "b"), "b"), "c"); // error! property "a" is missing
Run Code Online (Sandbox Code Playgroud)

在这里你可以看到编译器真的在照顾你,因为如果你cb()以错误的方式调用,你会得到一个错误告诉你。

不幸的是,的类型签名Array<T>.reduce()

reduce<U>(
  callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: readonly T[]) => U, 
  initialValue: U
): U;
Run Code Online (Sandbox Code Playgroud)

不足以表示每次callbackfn在数组元素上调用时发生的连续类型缩小。据我所知,没有办法改变它来做到这一点。您想说该callbackfn类型是一些疯狂的类型交集,对应于它对数组的每个连续成员的行为方式,例如((p: A, c: this[0])=>B) & ((p: B, c: this[1])=>C) & ((p: C, c: this[2])=>D) & ..., 对于泛型参数A, B, C,D等,然后希望编译器可以从您的参数中推断出这些参数拨电至reduce()。嗯,不能。这种高阶推理并不是该语言的一部分(至少从 TS3.7 开始)。

所以,这就是我们必须停下来的地方。您可以展开循环并调用cb(cb(cb(...,或者调用reduce()并使用类型断言。我认为类型断言实际上并没有那么糟糕;它专门针对您比编译器更聪明的情况......而这似乎就是其中之一。

好的,希望有帮助;祝你好运!

链接到代码