百科问答小站 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 多一些提示,让它推出来的结果更符合开发者的预期。




  

相关话题

  如何理解编程过程中的逻辑严谨性? 
  请问有什么书,可以浅显趣味的把前端原理讲的相当透彻呢? 
  网上常能见到的一段 JS 随机数生成算法如下,为什么用 9301, 49297, 233280 这三个数字做基数? 
  JavaScript里面有什么是不用刻意学的? 
  JavaScript、jQuery、AJAX、JSON 这四个之间的关系? 
  大型公司里外包员工和正式员工有什么区别? 
  如何看待 Reaktor Hello World 卫星将搭载 node.js 程序? 
  用python如何写网页?有好的教程码? 
  Vue 打包的 html 怎么样禁止打开 F12 开发者工具呢? 
  为什么一些公司招前端不想要培训班出来的人? 

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





© 2024-06-03 - tinynew.org. All Rights Reserved.
© 2024-06-03 - tinynew.org. 保留所有权利