继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Announcing TypeScript 2.8

千岁不倒翁
关注TA
已关注
手记 362
粉丝 60
获赞 386

TypeScript 2.8 在这里,我们带来一些你会十分喜爱的功能!

你熟悉 TypeScript 吗? 它是一种将可选的静态类型添加到JavaScript的语言。 这些静态类型会让你的代码不犯拼写错误以及其他的愚蠢错误。 基于围绕这些类型所构建的组件,还可以实现代码自动补全和项目索引等更多美妙的功能。 把你的代码交给 TypeScript 编译器运行后,将只剩下清晰,可读性强且符合标准的JavaScript代码。 也可以将你的代码重写为大部分仅支持 es5 甚至 es3 旧版浏览器所能运行的代码。想要了解 TypeScript 的更多相关信息 , 请查看我们的文档.

如果你现在急不可待,你可以通过 NuGet 下载或者运行以下指令:

npm install -g typescript

你能获得以下编辑器支持:

其他编辑器 的支持更新时间可能会不同, 但会尽快提供。

为了快速浏览我们在此版本中发布的内容,我们将其组合成列表放在一起便于浏览:

如果你升级版本,你需要记住一些细微部分的 调整 .

除此之外,让我们来看看 TypeScript 2.8带来的新功能吧!!

条件类型 (Conditional types)

条件类型是 TypeScript 中的一个新构造,它允许我们根据其他类型来选择类型。 它们的表现形式是:

A extends B ? C : D

其中a,b,c和d是所有类型。 你应将其看作 " 当类型a可赋值给b时,那么这种类型是c; 否则,它是d。" 如果你曾在JavaScript中使用过条件语法,你将会感到很熟悉。

我们举两个具体的例子:

interface Animal {
    live(): void;
}interface Dog extends Animal {
    woof(): void;
}// 类型是 'number'type Foo = Dog extends Animal ? number : string;// 类型是 'string'type Bar = RegExp extends Dog ? number : string;

你可能会想知道改变为什么会立即生效。我们可以知道 Foo 是数字,而 Bar 会是字符串,所以我们不妨明确地写出它。但条件类型的真正强大之处来自于它们与泛型的使用。

例如,让我们来看看以下几个方法:

interface Id { id: number, /* other fields */ }interface Name { name: string, /* other fields */ }declare function createLabel(id: number): Id;declare function createLabel(name: string): Name;declare function createLabel(name: string | number): Id | Name;

这些用于createlabel的重载,向我们展示了一个根据其输入类型作出选择的单个javascript函数。请注意以下两点:

  1. 如果一个资源必须在整个api中反复进行同样的选择,这会变得很麻烦。

  2. 我们必须创建三个重载:一个用于确定类型的每个案例,另一个用于最常见的案例。 对于我们必须处理的其他情况,重载次数会呈指数级增长。

相反,我们可以使用条件类型将我们的两个重载忽略到一个,并创建一个类型别名,以便我们可以重用该逻辑。

type IdOrName<T extends number | string>=T extends number ? Id : Name;
declare function createLabel<T extends number | string>(idOrName: T): T extends number ? Id : Name;
let a = createLabel("typescript");// Namelet b = createLabel(2.8);// Idlet c = createLabel("" as any);// Id | Namelet d = createLabel("" as never);// never

如同JavaScript可以根据一个值的特性在运行时做出决定一般,条件类型让 TypeScript 根据其他类型的特征在类型系统中作出判断。

作为另一个例子,我们也可以编写一个名为flatten的类型,将数组类型平移为它们的元素类型,但是另外保留它们:

// 如果我们有一个数组,当我们用 'number' 索引时,将会得到这个类型。
// 否则,单独留下类型。type Flatten`<T>`
= T extends any[] ? T[number] : T;

在条件类型内的推断

条件类型也为我们提供了一种方法,通过使用 infer 关键字从我们在真正分支中比较的类型推断出来。例如,我们可以在flatten中推断出元素类型,而不用去动手取出它

// 我们可以使用 '(infer U)[]' 而不是 'Array`<infer U>`'type Flatten`<T>`
= T extends Array`<infer U>`
? U : T;

在这里,我们已经声明式地引入了一个名为u 的新的泛型类型变量,而不是指定如何检索 t的元素类型。这使我们不再考虑如何获取想要的类型。

通过条件分配给联合体( unions )

当条件类型对单个类型参数起作用时,它们分布在各个联合体中。 所以在下面的例子中,Bar的类型是 string [] | number [] 因为Foo应用于联合类型string |number

type Foo`<T>`= T extends any ? T[] : never;
/**
 * Foo distributes on 'string | number' to the type
 *
 *    (string extends any ? string[] : never) |
 *    (number extends any ? number[] : never)
 * 
 * which boils down to
 *
 *    string[] | number[]
 */
type Bar = Foo<string | number>;

如果你想要避免在联合体(unions)中发布内容,则可以使用中括号括住“extends”关键字的两侧:

type Foo`<T>` = [T] extends [any] ? T[] : never;// Boils down to Array<string | number>type Bar = Foo<string | number>;

虽然条件类型起初可能看起来有点吓人,但当你需要进一步推动类型系统以获得准确类型时,我们相信它们将为你带来大量的灵活性。

新的内置助手

TypeScript 2.8 在lib.d.ts中提供了几种利用条件类型的新类型别名:

// 这些都已经内置到lib.d.ts中!/**
 * Exclude from T those types that are assignable to U
 */type Exclude`<T, U>` = T extends U ? never : T;/**
 * Extract from T those types that are assignable to U
 */type Extract`<T, U>` = T extends U ? T : never;/**
 * Exclude null and undefined from T
 */type NonNullable`<T>` = T extends null | undefined ? never : T;/**
 * Obtain the return type of a function type
 */type ReturnType`<T extends (...args: any[]) =>` any> = T extends (...args: any[]) => infer R ? R : any;/**
 * Obtain the return type of a constructor function type
 */type InstanceType`<T extends new (...args: any[]) =>` any> = T extends new (...args: any[]) => infer R ? R : any;

NonnullableReturntypeInstancetype相对不言而喻,ExcludeExtract则更有趣一些。

Extract 从第一个参数中选择用于给第二个参数分配的类型:

// string[] | number[]type Foo = Extract<boolean | string[] | number[], any[]>;

Exclude 则相反,它从第一个参数中删除不能分配给第二个参数的类型 :

// booleantype Bar = Exclude<boolean | string[] | number[], any[]>;

只有声明才会发出

感谢来自Manoj Patel一个pull请求,TypeScript 现在提供了一个--emitDeclarationOnly标志,当你有一个发送JavaScript文件的替代构建步骤时,可以使用这个标志,但需要单独发出声明文件。 在这种模式下,不会生成JavaScript文件和源代码文件;只是可以用于资源使用者的.d.ts文件。

其中一个用例就是使用备用编译器来处理TypeScript,例如babel 7.对于利用此标志的存储库示例,check out urql from Formidable Labs,或查看our Babel starter repo

@jsx 预处理指令(pragma comments)

通常,jsx的用户希望将其jsx标记重写为React.createElement。然而,如果你使用的库有类似React的工厂api,比如Preact, Stencil, Inferno, Cycle其他等,你可能想稍微调整一下。

曾经,TypeScript 只允许用户使用jsxFactory选项(以及不赞成使用的reactNamespace选项)在全局级别控制jsx的发送。但如果你想在同一个应用程序中混合使用这些库,那 jsx 将无法使用。

幸运的是,TypeScript 2.8现在允许你通过在文件顶部添加一个// @ jsx注释来逐个文件地设置你的jsx工厂。如果你在babel中使用了相同的功能,这应该看起来有些熟悉。

/** @jsx dom */import { dom } from "./renderer"<h></h>

上面的示例导入了一个名为dom的函数,并使用jsx编译指示选择dom作为文件中所有jsx表达式的工厂。TypeScript 2.8 在编译为commonjs和es5时会将其重写为以下内容:

var renderer_1 = require("./renderer");
renderer_1.dom("h", null);

JSX 通过jsx工厂解决

目前,当typescript使用jsx时,它会查找全局JSX命名空间来查找某些类型(例如“什么是jsx组件的类型?”)。在 TypeScript 2.8中,编译器将尝试根据jsx工厂的位置查找JSX命名空间。例如,如果您的jsx工厂是React.createElement,那么 TypeScript 将尝试首先解析React.JSX,然后解析当前范围内的JSX

当混合和匹配不同库(例如: React 和 Preact)或特定库的不同版本(例如: React14和React16)时,这可能是有用的,因为将jsx名称空间放置在全局范围中可能导致问题。

今后,我们建议新的面向jsx的库避免将JSX放在全局范围内,而是将它从相应的工厂函数的相同位置导出。然而,为了能够向后兼容,TypeScript 将在必要时继续回落到全局范围。

精确地控制映射类型的修饰符

TypeScript 的映射对象类型是一个非常强大的构造。它有个便利的功能:是允许用户创建新的类型,其中包含为其所有属性设置的修饰符。例如,以下类型创建一个基于T的新类型,并且T中的每个属性都变为只读和可选(?)。

// 创建一个包含 T 中所有属性的类型,,
// 但标记为只读和可选.type ReadonlyAndPartial`<T>` = {
    readonly [P in keyof T]?: T[P]
}

所以映射的对象类型可以添加修饰符,但直到此时,无法从T删除remove修饰符。

TypeScript 2.8提供了用-运算符去除映射类型中的修饰符的新语法,以及用+运算符添加修饰符的新的更加明确的语法。例如:

type Mutable`<T>` = {
    -readonly [P in keyof T]: T[P]
}interface Foo {
    readonly abc: number;
    def?: string;
}// 'abc'is no longer read-only, but 'def'is still optional.type TotallyMutableFoo = Mutable`<Foo>`

在上面的例子中,Mutable从它映射的类型的每个属性中删除readonly。 同样,TypeScript 现在在lib.d.ts中提供了一个新的Required类型,用于从每个属性中删除选项:

/**
 * Make all properties in T required
 */type Required`<T>` = {
    [P in keyof T]-?: T[P];
}

当你想调出一个映射类型添加修饰符时,+操作符可以很方便的实现。例如,我们从上面的ReadonlyAndPartial可以定义如下:

type ReadonlyAndPartial`<T>` = {
    +readonly [P in keyof T]+?: T[P];
}

组织导入(Organize imports)

TypeScript 的语言服务现在提供了组织导入的功能。此功能将删除所有未使用的导入,按文件路径对现有导入进行排序,并对命名导入进行排序。

修复未初始化的属性

TypeScript 2.7引入了对类中未初始化属性的额外检查,感谢来自Wenlu Wanga pull request。 TypeScript 2.8带来了一些有用的快速修复,使其更容易添加到您的代码库中。

突破性的改变

--noUnusedParameters下检查未使用的类型参数

未使用的类型参数先前在--noUnusedLocals下报告过,但是现在报告在--noUnusedParameters下。

HTMLObjectElement 不再具有 alt 属性

这种行为不包含在 WHATWG 标准中。

下一步是什么?

我们希望 TypeScript 2.8能够进一步推动 envelope 提供一种真正代表 JavaScript 作为语言本质的类型系统。因此,我们相信我们可以在你编码的过程中,为你创造出一种更高效、更快乐的体验。

在接下来的几周内,我们将更清楚地了解 TypeScript 2.9的存储情况,但是和往常一样,你可以留意 TypeScript 路线图 ,看看我们正在为下一个版本做些什么。你也可以尝试我们的夜间发布,今天就能体验未来!例如,通用的jsx元素已经出现在 TypeScript 最近的夜间版本中!

让我们知道你对这个版本的看法,请写在Twitter 上或者在下面的评论中, 并随时向我们提交建议和 a GitHub issue.

可劲儿造吧!(Happy Hacking!)

原文出处

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP