TypeScript基础之const断言

前言

文中内容都是参考https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions 以及 https://mariusschulz.com/blog/const-assertions-in-literal-expressions-in-typescript 内容。

const断言

TypeScript 3.4 引入了一种新的字面量构造方式,也称为 const 断言。
先看以下代码:

let str = 'hello';
// let str: string


const stringLiteral = "https"; 
// const stringLiteral: "https"

变量str实际为一种宽泛的字符串类型,只要是字符串,都可赋值给变量str, 即let关键字声明的变量会被推断为拓宽后的类型;
而变量stringLiteralconst关键字,其变量值不能进行修改, 所以推断为字面量类型是非常合适的。 它保留了赋值的准确类型信息;
综上,const关键字实际是将宽泛的类型,例如字符串,数字等转化为具体的值类型。 而as const则是将此特性用于断言之中,方便类型转换操作。

当然也可以使用尖括号断言语法:

let x = <const>"hello"; 
// let x: "hello"

使用

对基本类型使用

let str = 'hello' as const;
// let str: "hello"

str = '2';
// 不能将类型“"2"”分配给类型“"hello"”

let num = 100 as const;
// let num: 100

num = 1;
// 不能将类型“1”分配给类型“100”

相当于推断为具体的值类型

对数组使用

let arr1 = [1, 2, 3];
// let arr1: number[] 

let arr2 = [1, 2, 3] as const;
// let arr2: readonly [1, 2, 3] 

let arr3 = ['xman', 18];
// let arr3: (string | number)[]

arr3.push(false);
// 类型“false”的参数不能赋给类型“string | number”的参数

let arr4 = ['xman', 18] as const;
// let arr4: readonly ["xman", 18]

arr4.push(false);
// 类型“readonly ["xman", 18]”上不存在属性“push”

arr3数组被推断为(string | number)[], 因此不能添加除了string | number之外的类型。
arr4数组通过as const限定后,数组类型变为readonly ["xman", 18]

对对象字面量使用

let obj1 = { name: "xman", age: 18 };
// let obj1: {
//   name: string;
//   age: number;
// };

let obj2 = { name: "xman", age: 18 } as const;
// let obj2: {
//   readonly name: "xman";
//   readonly age: 18;
// };

对象字面量应用 const断言后, 对象字面量的属性,将使用 readonly 修饰
同样适用于包含引用类型的数组:

let arr1 = [
  { name: "xman", age: 18 },
  { name: "zxx", age: 22 },
];
// let arr1: {
//   name: string;
//   age: number;
// }[];
 

let arr2 = [
  { name: "xman", age: 18 },
  { name: "zxx", age: 22 },
] as const;
// let arr2: readonly [
//   {
//     readonly name: "xman";
//     readonly age: 18;
//   },
//   {
//     readonly name: "zxx";
//     readonly age: 22;
//   }
// ];

type T3 = typeof arr2[number]['name'];
// type T3 = "xman" | "zxx"

总结
使用这个断言后, TS就明白:

  • 表达式中的任何字面类型不应被拓宽
  • 对象字面量会获得readonly属性
  • 数组字面量会变成readonly元组

举例说明

示例1

假设我们已经编写了以下函数。它接受 URLHTTP 请求方法,使用浏览器的 Fetch API 向该 URL 发出 GETPOST请求,并将响应反序列化为 JSON:

function fetchJSON(url: string, method: "GET" | "POST") {
  return fetch(url, { method }).then(response => response.json());
}

调用时传入url及请求方法:

// 没毛病
fetchJSON("https://example.com/", "GET").then(data => {
  // ...
});

现在让我们做一点重构。HTTP 规范定义了各种其他请求方法,如 DELETE、HEAD、PUT 等。我们可以定义一个枚举样式的映射对象,并列出各种请求方法:HTTPRequestMethod

const HTTPRequestMethod = {
  CONNECT: "CONNECT",
  DELETE: "DELETE",
  GET: "GET",
  HEAD: "HEAD",
  OPTIONS: "OPTIONS",
  PATCH: "PATCH",
  POST: "POST",
  PUT: "PUT",
  TRACE: "TRACE",
};

现在我们可以将函数调用中的字符串文本GET替换为:fetchJSONHTTPRequestMethod.GET

// 类型“string”的参数不能赋给类型“"GET" | "POST"”的参数
fetchJSON("https://example.com/", HTTPRequestMethod.GET).then((data) => {
  // ...
});

根据之前内容我们可以知道HTTPRequestMethod.GET类型为string, 所以会出现类型“string”的参数不能赋给类型“"GET" | "POST"”的参数错误提示
这时我们可以使用const类型断言功能:

const HTTPRequestMethod = {
  CONNECT: "CONNECT",
  DELETE: "DELETE",
  GET: "GET",
  HEAD: "HEAD",
  OPTIONS: "OPTIONS",
  PATCH: "PATCH",
  POST: "POST",
  PUT: "PUT",
  TRACE: "TRACE",
} as const;

HTTPRequestMethod类型为:

const HTTPRequestMethod: {
  readonly CONNECT: "CONNECT";
  readonly DELETE: "DELETE";
  readonly GET: "GET";
  readonly HEAD: "HEAD";
  readonly OPTIONS: "OPTIONS";
  readonly PATCH: "PATCH";
  readonly POST: "POST";
  readonly PUT: "PUT";
  readonly TRACE: "TRACE";
};

此时再调用据不会出问题了:

// 没毛病
fetchJSON("https://example.com/", HTTPRequestMethod.GET).then((data) => {
  // ...
});

当然我们也可以使用枚举来解决:

enum HTTPRequestMethod {
  CONNECT = "CONNECT",
  DELETE = "DELETE",
  GET = "GET",
  HEAD = "HEAD",
  OPTIONS = "OPTIONS",
  PATCH = "PATCH",
  POST = "POST",
  PUT = "PUT",
  TRACE = "TRACE",
}

示例2

通过const类型断言, 省略一些只用于提示编译器不变性的类型

export const Colors = {
  red: "RED",
  blue: "BLUE",
  green: "GREEN",
} as const;


// 或者使用 'export default'
export default {
  red: "RED",
  blue: "BLUE",
  green: "GREEN",
} as const;

注意事项

  1. const断言只能用于简单的字面量表达式
// 以下会出错  
// A 'const' assertions can only be applied to references to enum members, or string, number, boolean, array, or object literals.
let a = (Math.random() < 0.5 ? 0 : 1) as const;
let b = (60 * 60 * 1000) as const;

// 以下没毛病!
let c = Math.random() < 0.5 ? (0 as const) : (1 as const);
let d = 3_600_000 as const;

  1. const 上下文不会立即将表达式转换为完全不可变
let arr = [1, 2, 3, 4];
let foo = {
  name: "foo",
  contents: arr,
} as const;

foo.name = "bar";
// Cannot assign to 'name' because it is a read-only property.

foo.contents = [];
// Cannot assign to 'contents' because it is a read-only property

foo.contents.push(5);   // 没毛病

参考资料

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions
https://mariusschulz.com/blog/const-assertions-in-literal-expressions-in-typescript

### 回答1: 当我们使用const关键字声明一个变量时,我们可以使用断言(assertion)来确保该变量的值不会被修改。在使用断言时,我们需要使用assert()函数,将要断言的条件作为参数传递给该函数。如果条件为真,则程序继续执行;如果条件为假,则程序会抛出一个assertion failed的错误,并停止执行。例如: const int x = 5; assert(x == 5); 在上面的例子中,我们声明了一个const int类型的变量x,并使用assert()函数来确保x的值为5。如果x的值不为5,则程序会停止执行,并抛出一个错误。 ### 回答2: const 断言是一种类型断言,它可以用来告诉编译器一个值的确切类型,并强制将该值视为指定的类型。 使用 const 断言的语法很简单,在值或变量之前加上 `as const` 即可。例如: ```typescript const obj = { a: "hello", b: "world" as const, }; ``` 在上述代码中,属性 `a` 的类型是字符串,而属性 `b` 的类型被使用 const 断言为只读字符串字面量类型。 使用 const 断言的好处是,它可以帮助我们更加准确地定义和控制变量的类型,并在编译期间进行更严格的类型检查。这样可以减少运行时的错误,并增加代码的可读性和可维护性。 需要注意的是,const 断言只能用于字面量类型,即通过字面量赋值创建的类型,例如字符串、数字、对象字面量等。对于其他类型的变量,const 断言会被忽略。 ### 回答3: const 断言TypeScript 中的一种类型断言,用于明确指定某个变量的类型。 在 TypeScript 中,变量的类型可以由编译器进行推断,但有时候需要显式指定变量的类型,这个时候就可以使用 const 断言。 在使用 const 断言时,需要在变量后面加上 `as const`,表示告诉编译器该变量的类型是不可变的。 例如: ``` const name = '张三' as const; const age = 18 as const; ``` 在上面的例子中,name 的类型被指定为不可变的字符串类型,age 的类型被指定为不可变的数值类型。这样一来,name 和 age 的值不能被改变。 使用 const 断言的好处是能够提供更准确的类型信息,而且可以避免在一个值不会改变的情况下,被错误地赋予其他类型的值。 需要注意的是,const 断言只能在编译时进行类型推断,运行时不会对变量进行检查。因此,如果在声明之后对 const 断言的变量进行修改,仍然是合法的,只不过 TypeScript 编译器不会对此进行检查。 总之,const 断言是一种用于明确指定变量类型的工具,在需要确保变量不会被修改时,可以使用它来声明不可变的变量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值