百科问答小站 logo
百科问答小站 font logo



Typescript 如何使一个传入的 Array 类型变为元组类型? 第1页

  

user avatar   browsnet 网友的相关建议: 
      

题主这里的[1, 'hello', { a: 1 }] 是一个数组字面量 ,先说一下当开发者写下一些字面量的时候,TypeScript是怎么推导出对应类型的。

字面量的类型推断一直是不好设计的,因为同一个字面量可以被推断成不同的类型,test(1)里的第一个参数类型可以被推断成 11 | 2number 甚至是其他。

所以字面量类型推断需要配合 上下文类型 来协组推断,举例来说,你写了

       declare function test1<T>(a:T,b:T):any; test1(1,2)      

那么上面的a和b都会被推断成number,这是TypeScript认为最符合开发者预期的类型,但是当你改成

       declare function test2<T extends number>(a:T,b:T):any; test2(1,2)      

此时a 和 b都会被推断成1 | 2 这样更窄的类型,这里就是 上下文类型 协组字面量来做类型推断了。


回到题主的问题,题主的例子

       function watch<T extends unknown[]>(source: [...T], cb: (params: T) => void){}; const watcher = watch([1, 'hello', { a: 1 }], (params)=>{})      

题主可能认为,这里的 source 的[...T] 只是说把source 类型推断成一个Tuple Type,而 T 本身应该是一个形如(number | string | {a: number})[]Array Type

但是这里的字面量[1, 'hello', { a: 1 }]并不是独立推断的, 它受到上下文类型 的影响,而这个影响是是 Variadic tuple types 设计的一项特性。

当一个数组字面量的上下文类型是 Tuple Type,那么就会对这个数组字面量推导出对应的Tuple Type,[...T]就是这个上下文类型的指示器。

TS的作者 Anders 老爷子在提交这项特性的PR里提到过这一特点,并给出示例:

       declare function ft1<T extends unknown[]>(t: T): T; declare function ft2<T extends unknown[]>(t: T): readonly [...T]; declare function ft3<T extends unknown[]>(t: [...T]): T; declare function ft4<T extends unknown[]>(t: [...T]): readonly [...T];  ft1(['hello', 42]);  // (string | number)[] ft2(['hello', 42]);  // readonly (string | number)[] ft3(['hello', 42]);  // [string, number] ft4(['hello', 42]);  // readonly [string, number]      

可以注意到这里的 [...T] 作为Tuple Type 的指示器,它改编了对 数组字面量 原始类型的推断,有了这个上下文类型,['hello', 42] 的原始类型不再被推断成 (string | number)[] ,而是被推成了 [string, number]


考虑这样的一个例子

       declare function test3<T extends unknown[]>(t: [string,...T]): T; test3(['hello', 42]); // [number]      

这里的T类型是[number],这是因为['hello', 42] 在看到上下文类型是Tuple Like 的时候,就把字面量类型推断成了[string,number] 这种Tuple类型。然后再利用[string,number] extends [string,...infer T] 来找出T 的类型,也就是[number]

那么数组的字面量,除了被推断成Array Type<Union Type>Tuple Type 以外,还会有其它的结果吗?答案暂时是否定的,它目前只有这两种推断方式。

在当下的checker.ts 源代码里可以找到对应的实现function checkArrayLiteral

       if (forceTuple || inConstContext || contextualType && someType(contextualType, isTupleLikeType)) {    return createArrayLiteralType(createTupleType(elementTypes, elementFlags, /*readonly*/ inConstContext)); } return createArrayLiteralType(createArrayType(elementTypes.length ?      getUnionType(sameMap(elementTypes, (t, i) => elementFlags[i] & ElementFlags.Variadic ? getIndexedAccessTypeOrUndefined(t, numberType) || anyType : t), UnionReduction.Subtype) :       strictNullChecks ? implicitNeverType : undefinedWideningType, inConstContext));      

可以看出来,在特定的条件里(比如上下文类型是isTupleLikeType),数组的字面量可以被推断成Tuple Type ,否则它会被推断成 Array Type<Union Type>


结语:字面量的推断充满着大量的人工设计,因为一个字面量的类型拥有无限的可能性,TypeScript 在推导字面量的时候,对开发者的开发体验非常看重,它想着尽可能的让推断出来的类型符合开发者的预期 ,但是有时候不容易做到,上下文类型相当于给 TypeScript 多一些提示,让它推出来的结果更符合开发者的预期。




  

相关话题

  阿里的 UI 设计和 Google Microsoft Apple 的设计是否有差距? 
  学习编程照着别人的代码敲进去有效率吗? 
  Promise 不能被取消,真的算是它的缺点吗? 
  如何衡量一个人的 JavaScript 水平? 
  JavaScript 有必要缓存 for 循环中的 Array.length 吗? 
  如何看待程序员不写注释? 
  如何去判断 JS 字符串中含某个字符串? 
  如何评价华为新开源的鸿蒙方舟JS运行时(Ark JS Runtime)? 
  jquery的几个小bug,大家帮忙分析下? 
  前端当前是否处在一个糟糕的时代? 

前一个讨论
说脏话是不是一种双商偏低的行为?
下一个讨论
moment.js为何要设计成mutable的,有何优缺点?





© 2025-05-04 - tinynew.org. All Rights Reserved.
© 2025-05-04 - tinynew.org. 保留所有权利