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




  

相关话题

  程序员讨厌面试被问一些基础问题么? 
  HTML 静态页面的头部和底部都是相同的,如何让每个页面统一调用一个公共的头部和底部呢? 
  为什么能够提供BFC的inline-block元素却不能嵌套block元素? 
  为什么JavaScript不能成为自己的标准,非得冒出ES6成为JavaScript的标准呢? 
  为什么前端框架 react、vue 里会出现生命周期的说法? 
  前端开发的难点到底在什么地方? 
  为什么form表单提交没有跨域问题,但ajax提交有跨域问题? 
  为什么能够提供BFC的inline-block元素却不能嵌套block元素? 
  前端因为像素还原设计稿而离职,这是个别现象吗? 
  JavaScript里面有什么是不用刻意学的? 

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





© 2024-11-25 - tinynew.org. All Rights Reserved.
© 2024-11-25 - tinynew.org. 保留所有权利