1、引入
在介绍 as const 之前,得先介绍点其他的,以便更好的理解。
1.1、只读
在 Typescript 中,readonly 表示只读,可以在 interface 和 type 中标记属性
举例:对象
type Foo = { readonly name: string; readonly want: string; }; const foo: Foo = { name: '下雪天的夏风', want: '求关注' }; // 报错:无法分配到 "name" ,因为它是只读属性。ts(2540) foo.name = 456;
举例:数组
const arr: readonly string[] = ["a", "b", "c"]; // 报错:类型“readonly string[]”上不存在属性“push”。ts(2339) arr.push('d') // 报错:类型“readonly string[]”中的索引签名仅允许读取。ts(2542) arr[1]= 'r'
readonly string[] 和 ReadonlyArray<string> 是等价的。
1.2、元组
在 JavaScript 中,数组的元素可以是任意类型。而在 Typescript 中,如果数组的元素类型不一致,此时这个数组称为元祖(Tuple)。
元祖类型:元素数量和类型都确定的数组。
可以理解为:既然能确定每个元素的类型,那数量也就确定了。
let x: [string, number]; x = ['hello', 10]; // ok x = [10, 'hello']; // Error
在元祖中,不能访问越界元素,但可以通过push等方法扩展,但扩展元素的类型会使用联合类型替代。
x[0] = '你好' // ok // 报错:长度为 "2" 的元组类型 "[string, number]" 在索引 "2" 处没有元素。ts(2493) x[2] = 'xx' // 报错:类型“boolean”的参数不能赋给类型“string | number”的参数。ts(2345) x.push(true) x.push('world') // ok
1.3、只读的元祖
下面的2种写法,因为只读的原因,二者在使用上并没有区别,都不能通过索引和方法修改。
但Typescript编译是会区分:数组类型和元素类型的,下面的例子中会有介绍。
// 只读数组 const list: readonly string[] = ['a', 'b'] // 只读元祖 const list: readonly [string, string] = ['a', 'b']
as const 就是在只读的元祖类型上再次提高精度:类型精确到值。
所以,as const得到是一个只读的元祖类型!
2、as const
2.1、需求
对于上面的list来说,获取的数组元素的类型是string
// string type tList = typeof list[number]
实际工作中,会遇到这样的需求:“要求某个变量的取值范围,限制某个数组范围内。”
以上面定义的 list 为例,需要一个这样的type
type tList = 'a' | 'b' function getItem(item: tList) {}
2.2、as const 的效果
而 as const 就可以实现这个需求。我们对比下,注意listB的类型
// 只读元祖 const listA: readonly [string, string] = ['a', 'b'] // listB 的类型是:readonly ["a", "b"] const listB = ['a', 'b'] as const // string type tListA = typeof listA[number] // 'a' | 'b' type tListB = typeof listB[number]
typeof listA[number]的读取顺序:typeof listA 再[number]
2.3、as const 的其他使用例子
1、数组解构
const arr = ['abc', 123] const [p, q] = arr // Property 'toUpperCase' does not exist on type 'string | number'. // Property 'toUpperCase' does not exist on type 'number'.ts(2339) p.toUpperCase()
可以看到,当我们能确定 p 是string类型时,还是会报错。
解决办法就是as const
const arr = ['abc', 123] as const
2、扩展参数(展开语法)
在 Typescript 中,使用扩展参数时有2点要求,满足之一即可。
扩展参数必须具有元组类型
扩展参数需要传递给剩余参数
举例
const args = [1, 2]; function testArgs(a: number, b: number) {} // 报错:扩展参数必须具有元组类型或传递给剩余参数.ts(2556) testArgs(...args)
解决方法1
// 扩展参数必须具有元组类型 const args: [number, number] = [1, 2]; // 注意,这不是元祖类型!所以依旧会报错 const args: number[] = [1, 2];
而这个元祖类型也可以用as const 来解决
const args = [1, 2] as const;
解决方法2
// 扩展参数需要传递给剩余参数 function testArgs(...theArgs: number[]) {}