背景
前段时间发布了 Typescript 5.0 beta 版,预计3月14号发布正式版,我们来一起看看有哪些新特性。
装饰器
TS5 支持的是 Stage3 装饰器,进入到 Stage3 的特性基本上就可以认为可以加入 JS 标准了,更多内容可以看下之前我整理的文档:再来了解一下装饰器
const 类型参数
Typescript 通常会把一个对象推断成一个更通用的类型。比如下面的例子,推断出的 names 是 string[];
type HasNames = { names: readonly string[] };
function getNamesExactly<T extends HasNames>(arg: T): T["names"] {
return arg.names;
}
// 推断的类型是: string[]
const names = getNamesExactly({ names: ["Alice", "Bob", "Eve"]});
假如我们希望推断的类型是 [“Alice”, “Bob”, “Eve”],就需要用 as const 转化一下;
// 推断的类型是 ["Alice", "Bob", "Eve"]
const names2 = getNamesExactly({ names: ["Alice", "Bob", "Eve"]} as const);
这种方式用起来不够优雅而且可能会忘记加,所以 TS5 的类型参数支持了 const 描述符;
type HasNames = { names: readonly string[] };
function getNamesExactly<const T extends HasNames>(arg: T): T["names"] {
return arg.names;
}
// 推断类型: readonly ["Alice", "Bob", "Eve"]
// 这样就不需要 as const 了
const names = getNamesExactly({ names: ["Alice", "Bob", "Eve"] });
不过需要注意的是,如果约束是可变类型,会存在一些问题;
什么是可变类型?举个例子:Array vs ReadonlyArray,前者是可变类型,后者是不可变类型
declare function fnBad<const T extends string[]>(args: T): void;
// T 仍然是 string[],因为 readonly ["a", "b", "c"] 不能赋值给 string[]
fnBad(["a", "b" ,"c"]);
本来推断出的 T 应该是 readonly [“a”, “b”, “c”],但是 readonly [“a”, “b”, “c”] 不能赋值给 string[],所以 T 的类型回退成了 string[];
解决的办法,也很简单,使用 readonly string[] 替换 string[]:
declare function fnGood<const T extends readonly string[]>(args: T): void;
// T 是 readonly ["a", "b", "c"]
fnGood(["a", "b" ,"c"]);
还有一点需要注意,const 修饰符只能影响直接写在函数调用中的对象、数组和原始表达式的推断,举个例子:
declare function fnGood<const T extends readonly string[]>(args: T): void;
const arr = ["a", "b" ,"c"];
// T 仍然是 string[] -- const 修饰符在这里没有任何效果
fnGood(arr);
// T 是 readonly string ["a", "b" ,"c"]
fnGood(["a", "b" ,"c"]);
所有枚举都是 union 枚举
TS 最初设计的枚举类型比较简单,除了 E.Foo and E.Bar 只能赋值给 E 类型的变量之外,这些枚举类型的成员其实就是数字。
enum E {
Foo = 10,
Bar = 20,
}
TS2.0 引入了枚举字面量,它给每个成员都分配了一个类型,那么这个枚举类型就变成了由所有成员类型组成的 union 类型,我们把它叫做 union enum。
// Color 就类似于一个 Union类型:Red | Orange | Yellow | Green | Blue | Violet
enum Color {
Red, Orange, Yellow, Green, Blue, Violet
}
union enum 的优势在于,我们可以使用这个枚举类型的子集。如下,Color 包含六个成员,我们可以定义包含三个成员的子集类型。
enum Color {
Red, Orange, Yellow, Green, Blue, Violet
}
// 每个枚举成员都有自己的类型
// 定义一个只包含三个成员的 Union 类型,相当于
type PrimaryColor = Color.Red | Color.Green | Color.Blue;
但是枚举成员的类型与枚举成员的值是强相关的。如果枚举成员的值是函数,就无法计算出成员的值,所以就没办法给每个成员分配对应的类型,也就没办法将枚举转变成 union enum。
TS5 解决了这个问题,即使成员的值是函数,也能为其创建唯一的类型;每个枚举类型都是 union enum。
枚举类型的两个新报错
- 给枚举变量赋值成员值以外的值时会报错
enum SomeEvenDigit {
Zero = 0,
Two = 2,
Four = 4
}
// 错误,1 不是成员的值
let m: SomeEvenDigit = 1;
- 成员值是 string/number 混合并且存在间接赋值的场景
enum Letters {
A = "a"
}
enum Numbers {
one = 1,
two = Letters.A
}
// 错误
const t: number = Numbers.two;
支持 export type *
TS3.8 支持了针对 type 的导入,TS5 在此基础上扩展出了 export * from “module” 或者 export * as ns from “module”
// models/vehicles.ts
export class Spaceship {
// ...
}
// models/index.ts
export type * as vehicles from "./spaceship";
// main.ts
import { vehicles } from "./models";
function takeASpaceship(s: vehicles.Spaceship) {
// 这里没问题 - vehicles 只能当成类型使用
}
function makeASpaceship() {
return new vehicles.Spaceship();
// ^^^^^^^^
// vehicles 不能当成值使用,因为它是通过 export type 导出的.
}
JSDoc 支持 @satisfies
TS4.9 引入了 satisfies 操作符,它可以保证变量兼容某个类型,比如
interface NewType {
name: string;
hobby: string | string[]
}
/** * a 被推断为 * { * name: string; * hobby: string; *. } */
let a: NewType = {
name: 'name1',
hobby: 'one hobby'
};
(hobby as string).toLower
TS5 让 JSDoc 上也支持了 satisfies
// @ts-check
/** * @typedef NewType * @prop {string} [name] * @prop {string | string[]} [hobby] */
/** * @satisfies {NewType} */
let a = {
name: 'name1',
hobby: 'one hobby'
};
JSDoc 支持 @overload
JSDoc 通过 @overload 支持函数重载。
TS
// 函数重载:
function printValue(str: string): void;
function printValue(num: number, maxFractionDigits?: number): void;
// 函数定义:
function printValue(value: string | number, maximumFractionDigits?: number) {
if (typeof value === "number") {
const formatter = Intl.NumberFormat("en-US", {
maximumFractionDigits,
});
value = formatter.format(value);
}
console.log(value);
}
JSDoc
// @ts-check
/** * @overload * @param {string} value * @return {void} */
/** * @overload * @param {number} value * @param {number} [maximumFractionDigits] * @return {void} */
/** * @param {string | number} value * @param {number} [maximumFractionDigits] */
function printValue(value, maximumFractionDigits) {
if (typeof value === "number") {
const formatter = Intl.NumberFormat("en-US", {
maximumFractionDigits,
});
value = formatter.format(value);
}
console.log(value);
}
extends 支持多个配置文件
当维护多个项目时,通常每个项目的 tsconfig.json 都会继承于一份基准配置。为了提高 extends 的灵活性,TS5 支持集成多个配置文件。
// tsconfig1.json
{
"compilerOptions": {
"strictNullChecks": true
}
}
// tsconfig2.json
{
"compilerOptions": {
"noImplicitAny": true
}
}
// tsconfig.json
{
"extends": ["./tsconfig1.json", "./tsconfig2.json"],
"compilerOptions": {
},
"files": ["./index.ts"]
}
customConditions
假设有一个第三方包的 package.json 包含如下代码:
{
// ...
"exports": {
"my-condition": "./foo.mjs",
"node": "./bar.mjs", // 用于 node 环境
"import": "./baz.mjs", // 通过 import/import() 引入时使用
"require": "./biz.mjs" // 通过 require 引入时使用
}
}
并且你项目中的 tsconfig.json 是这样:
{
"compilerOptions": {
"target": "es2022",
"moduleResolution": "bundler",
"customConditions": ["my-condition"]
}
}
此时,如果在你项目中 import 了这个第三方包,实际导入的是这个入口 foo.mjs。
关系型运算符禁止隐式类型转换
TS5 之前已经禁止了算数运算符的隐式转换,下面的代码会有报错
// TS5 之前和之后都会报错
function func(ns: number | string) {
return ns * 4; // 错误, 可能存在隐式转换
}
TS5 新增了禁止关系运算符中的隐式类型转换
function func(ns: number | string) {
return ns > 4; // 错误, 可能存在隐式转换
}
// 需要做一次显示类型转换
function func(ns: number | string) {
return +ns > 4; // 正确
}
其他
- switch/case 自动补全 value 的所有未覆盖的字面量类型;
-
针对 –build 可以指定特定产物的标志,比如:打包产物需要包含类型声明文件tsc –build –p ./my-project-dir –declaration
-
–verbatimModuleSyntax 简化在编译产物里对于 import 的剔除策略
// 在编译产物里,整体剔除
import type { A } from "a";
// 在编译产物里改写成 'import { b } from "bcd";'
import { b, type c, type d } from "bcd";
// 在编译产物里改写成 'import {} from "xyz";'
import { type xyz } from "xyz";
-
–moduleResolution为了支持更多打包场景,在 node16/nodenext 基础上,新增了 bundler;
-
编译速度、打包体积都有很明显的优化。
如何体验 TS5?
第一步:通过 npm 安装 ts beta:
npm install typescript@beta
第二步:安装 VSCode 扩展:JavaScript and TypeScript Nightly – Visual Studio Marketplace
第三步:在 VSCode 中选择 TS 版本(Command + Shift + P)
参考
announcing-typescript-5-0-beta
satisfies:dev.to
欢迎一起交流。
今天的文章Typescript 5 来了分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/23133.html