diff --git a/PREFACE.md b/PREFACE.md index 0c31035a..3fa126a9 100644 --- a/PREFACE.md +++ b/PREFACE.md @@ -1,6 +1,6 @@ # 前言 -> Repo: https://github.com/zhongsp/TypeScript +> Repo: [https://github.com/zhongsp/TypeScript](https://github.com/zhongsp/TypeScript) 该工程是对 TypeScript 官方及开源社区书写的编程手册、版本发布说明等综合内容的中文翻译。 感谢 Microsoft 和开源社区的工程师们的工作,为 JavaScript 开发带来了全新的体验! diff --git a/lint.js b/lint.js index 729ee081..82276a6f 100644 --- a/lint.js +++ b/lint.js @@ -9,7 +9,7 @@ var options = { MD001: false, // Header levels should only increment by one level at a time MD002: false, // First header should be a h1 header MD003: "atx", // Header style - MD004: { style: "asterisk" }, // Unordered list style + MD004: { style: "consistent" }, // Unordered list style MD005: true, // Inconsistent indentation for list items at the same level MD006: true, // Consider starting bulleted lists at the beginning of the line MD007: { indent: 2 }, // Unordered list indentation @@ -30,7 +30,7 @@ var options = { MD026: { punctuation: ".,;:!" }, // Trailing punctuation in header MD027: true, // Multiple spaces after blockquote symbol MD028: true, // Blank line inside blockquote - MD029: { style: "ordered" }, // Ordered list item prefix + MD029: { style: "one_or_ordered" }, // Ordered list item prefix MD030: true, // Spaces after list markers MD031: true, // Fenced code blocks should be surrounded by blank lines MD032: true, // Lists should be surrounded by blank lines diff --git a/zh/breaking-changes/README.md b/zh/breaking-changes/README.md index 93ad2432..96cc1493 100644 --- a/zh/breaking-changes/README.md +++ b/zh/breaking-changes/README.md @@ -1,24 +1,23 @@ # Breaking Changes -* [TypeScript 3.6](typescript-3.6.md) -* [TypeScript 3.5](typescript-3.5.md) -* [TypeScript 3.4](typescript-3.4.md) -* [TypeScript 3.2](typescript-3.2.md) -* [TypeScript 3.1](typescript-3.1.md) -* [TypeScript 3.0](typescript-3.0.md) -* [TypeScript 2.9](typescript-2.9.md) -* [TypeScript 2.8](typescript-2.8.md) -* [TypeScript 2.7](typescript-2.7.md) -* [TypeScript 2.6](typescript-2.6.md) -* [TypeScript 2.4](typescript-2.4.md) -* [TypeScript 2.3](typescript-2.3.md) -* [TypeScript 2.2](typescript-2.2.md) -* [TypeScript 2.1](typescript-2.1.md) -* [TypeScript 2.0](typescript-2.0.md) -* [TypeScript 1.8](typescript-1.8.md) -* [TypeScript 1.7](typescript-1.7.md) -* [TypeScript 1.6](typescript-1.6.md) -* [TypeScript 1.5](typescript-1.5.md) -* [TypeScript 1.4](typescript-1.4.md) -* [TypeScript 1.1](typescript-1.1.md) - +- [TypeScript 3.6](typescript-3.6.md) +- [TypeScript 3.5](typescript-3.5.md) +- [TypeScript 3.4](typescript-3.4.md) +- [TypeScript 3.2](typescript-3.2.md) +- [TypeScript 3.1](typescript-3.1.md) +- [TypeScript 3.0](typescript-3.0.md) +- [TypeScript 2.9](typescript-2.9.md) +- [TypeScript 2.8](typescript-2.8.md) +- [TypeScript 2.7](typescript-2.7.md) +- [TypeScript 2.6](typescript-2.6.md) +- [TypeScript 2.4](typescript-2.4.md) +- [TypeScript 2.3](typescript-2.3.md) +- [TypeScript 2.2](typescript-2.2.md) +- [TypeScript 2.1](typescript-2.1.md) +- [TypeScript 2.0](typescript-2.0.md) +- [TypeScript 1.8](typescript-1.8.md) +- [TypeScript 1.7](typescript-1.7.md) +- [TypeScript 1.6](typescript-1.6.md) +- [TypeScript 1.5](typescript-1.5.md) +- [TypeScript 1.4](typescript-1.4.md) +- [TypeScript 1.1](typescript-1.1.md) diff --git a/zh/breaking-changes/typescript-1.4.md b/zh/breaking-changes/typescript-1.4.md index f311864f..22cf7d78 100644 --- a/zh/breaking-changes/typescript-1.4.md +++ b/zh/breaking-changes/typescript-1.4.md @@ -27,20 +27,28 @@ var bs: { x: number; y?: number; z?: number }[] = [b, a]; ## 泛型接口 -当在多个T类型的参数上使用了不同的类型时会得到一个错误,就算是添加约束也不行: +当在多个 T 类型的参数上使用了不同的类型时会得到一个错误,就算是添加约束也不行: ```typescript -declare function foo(x: T, y:T): T; -var r = foo(1, ""); // r used to be {}, now this is an error +declare function foo(x: T, y: T): T; +var r = foo(1, ''); // r used to be {}, now this is an error ``` 添加约束: ```typescript -interface Animal { x } -interface Giraffe extends Animal { y } -interface Elephant extends Animal { z } -function f(x: T, y: T): T { return undefined; } +interface Animal { + x; +} +interface Giraffe extends Animal { + y; +} +interface Elephant extends Animal { + z; +} +function f(x: T, y: T): T { + return undefined; +} var g: Giraffe; var e: Elephant; f(g, e); @@ -51,17 +59,19 @@ f(g, e); **推荐** 如果这种不匹配的行为是故意为之,那么明确指定类型参数: ```typescript -var r = foo<{}>(1, ""); // Emulates 1.0 behavior -var r = foo(1, ""); // Most useful -var r = foo(1, ""); // Easiest +var r = foo<{}>(1, ''); // Emulates 1.0 behavior +var r = foo(1, ''); // Most useful +var r = foo(1, ''); // Easiest f(g, e); ``` -_或_重写函数定义指明就算不匹配也没问题: +*或*重写函数定义指明就算不匹配也没问题: ```typescript -declare function foo(x: T, y:U): T|U; -function f(x: T, y: U): T|U { return undefined; } +declare function foo(x: T, y: U): T | U; +function f(x: T, y: U): T | U { + return undefined; +} ``` ## 泛型剩余参数 @@ -69,48 +79,52 @@ function f(x: T, y: U): T|U { return undefin 不能再使用混杂的参数类型: ```typescript -function makeArray(...items: T[]): T[] { return items; } -var r = makeArray(1, ""); // used to return {}[], now an error +function makeArray(...items: T[]): T[] { + return items; +} +var r = makeArray(1, ''); // used to return {}[], now an error ``` `new Array(...)`也一样 -**推荐** 声明向后兼容的签名,如果1.0的行为是你想要的: +**推荐** 声明向后兼容的签名,如果 1.0 的行为是你想要的: ```typescript function makeArray(...items: T[]): T[]; function makeArray(...items: {}[]): {}[]; -function makeArray(...items: T[]): T[] { return items; } +function makeArray(...items: T[]): T[] { + return items; +} ``` ## 带类型参数接口的重载解析 ```typescript var f10: (x: T, b: () => (a: T) => void, y: T) => T; -var r9 = f10('', () => (a => a.foo), 1); // r9 was any, now this is an error +var r9 = f10('', () => a => a.foo, 1); // r9 was any, now this is an error ``` **推荐** 手动指定一个类型参数 ```typescript -var r9 = f10('', () => (a => a.foo), 1); +var r9 = f10('', () => a => a.foo, 1); ``` ## 类声明与类型表达式以严格模式解析 -ECMAScript 2015语言规范\(ECMA-262 6th Edition\)指明_ClassDeclaration_和_ClassExpression_使用严格模式。 因此,在解析类声明或类表达式时将使用额外的限制。 +ECMAScript 2015 语言规范\(ECMA-262 6th Edition\)指明*ClassDeclaration*和*ClassExpression*使用严格模式。 因此,在解析类声明或类表达式时将使用额外的限制。 例如: ```typescript -class implements {} // Invalid: implements is a reserved word in strict mode +class implements {} // Invalid: implements is a reserved word in strict mode class C { - foo(arguments: any) { // Invalid: "arguments" is not allow as a function argument - var eval = 10; // Invalid: "eval" is not allowed as the left-hand-side expression - arguments = []; // Invalid: arguments object is immutable - } + foo(arguments: any) { + // Invalid: "arguments" is not allow as a function argument + var eval = 10; // Invalid: "eval" is not allowed as the left-hand-side expression + arguments = []; // Invalid: arguments object is immutable + } } ``` 关于严格模式限制的完整列表,请阅读 Annex C - The Strict Mode of ECMAScript of ECMA-262 6th Edition。 - diff --git a/zh/breaking-changes/typescript-1.5.md b/zh/breaking-changes/typescript-1.5.md index 896a7b6f..c1bb0842 100644 --- a/zh/breaking-changes/typescript-1.5.md +++ b/zh/breaking-changes/typescript-1.5.md @@ -4,13 +4,13 @@ ## 不允许在箭头函数里引用`arguments` -这是为了遵循ES6箭头函数的语义。之前箭头函数里的`arguments`会绑定到箭头函数的参数。参照[ES6规范草稿](http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts) 9.2.12,箭头函数不存在`arguments`对象。 从TypeScript 1.5开始,在箭头函数里使用`arguments`会被标记成错误以确保你的代码转成ES6时没语义上的错误。 +这是为了遵循 ES6 箭头函数的语义。之前箭头函数里的`arguments`会绑定到箭头函数的参数。参照[ES6 规范草稿](http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts) 9.2.12,箭头函数不存在`arguments`对象。 从 TypeScript 1.5 开始,在箭头函数里使用`arguments`会被标记成错误以确保你的代码转成 ES6 时没语义上的错误。 **例子:** ```typescript function f() { - return () => arguments; // Error: The 'arguments' object cannot be referenced in an arrow function. + return () => arguments; // Error: The 'arguments' object cannot be referenced in an arrow function. } ``` @@ -19,26 +19,30 @@ function f() { ```typescript // 1. 使用带名字的剩余参数 function f() { - return (...args) => { args; } + return (...args) => { + args; + }; } // 2. 使用函数表达式 function f() { - return function(){ arguments; } + return function () { + arguments; + }; } ``` ## 内联枚举引用的改动 -对于正常的枚举,在1.5之前,编译器_仅会_内联常量成员,且成员仅在使用字面量初始化时才被当做是常量。这在判断检举值是使用字面量初始化还是表达式时会行为不一致。从TypeScript 1.5开始,所有非const枚举成员都不会被内联。 +对于正常的枚举,在 1.5 之前,编译器*仅会*内联常量成员,且成员仅在使用字面量初始化时才被当做是常量。这在判断检举值是使用字面量初始化还是表达式时会行为不一致。从 TypeScript 1.5 开始,所有非 const 枚举成员都不会被内联。 **例子:** ```typescript -var x = E.a; // previously inlined as "var x = 1; /*E.a*/" +var x = E.a; // previously inlined as "var x = 1; /*E.a*/" enum E { - a = 1 + a = 1, } ``` @@ -46,74 +50,75 @@ enum E { ## 上下文的类型将作用于`super`和括号表达式 -在1.5之前,上下文的类型不会作用于括号表达式内部。这就要求做显示的类型转换,尤其是在_必须_使用括号来进行表达式转换的场合。 +在 1.5 之前,上下文的类型不会作用于括号表达式内部。这就要求做显示的类型转换,尤其是在*必须*使用括号来进行表达式转换的场合。 在下面的例子里,`m`具有上下文的类型,它在之前的版本里是没有的。 ```typescript -var x: SomeType = (n) => ((m) => q); -var y: SomeType = t ? (m => m.length) : undefined; +var x: SomeType = n => m => q; +var y: SomeType = t ? m => m.length : undefined; class C extends CBase { - constructor() { - super({ - method(m) { return m.length; } - }); - } + constructor() { + super({ + method(m) { + return m.length; + }, + }); + } } ``` 更多信息,查看[\#1425](https://github.com/Microsoft/TypeScript/issues/1425)和[\#920](https://github.com/Microsoft/TypeScript/issues/920)。 -## DOM接口的改动 +## DOM 接口的改动 -TypeScript 1.5改进了`lib.d.ts`库里的DOM类型。这是自TypeScript 1.0以来第一次大的改动;为了拥抱标准DOM规范,很多特定于IE的定义被移除了,同时添加了新的类型如Web Audio和触摸事件。 +TypeScript 1.5 改进了`lib.d.ts`库里的 DOM 类型。这是自 TypeScript 1.0 以来第一次大的改动;为了拥抱标准 DOM 规范,很多特定于 IE 的定义被移除了,同时添加了新的类型如 Web Audio 和触摸事件。 **变通方案:** -你可以使用旧的`lib.d.ts`配合新版本的编译器。你需要在你的工程里引入之前版本的一个拷贝。这里是[本次改动之前的lib.d.ts文件\(TypeScript 1.5-alpha\)](https://github.com/Microsoft/TypeScript/blob/v1.5.0-alpha/bin/lib.d.ts)。 +你可以使用旧的`lib.d.ts`配合新版本的编译器。你需要在你的工程里引入之前版本的一个拷贝。这里是[本次改动之前的 lib.d.ts 文件\(TypeScript 1.5-alpha\)](https://github.com/Microsoft/TypeScript/blob/v1.5.0-alpha/bin/lib.d.ts)。 **变动列表:** -* 属性`selection`从`Document`类型上移除 -* 属性`clipboardData`从`Window`类型上移除 -* 删除接口`MSEventAttachmentTarget` -* 属性`onresize`,`disabled`,`uniqueID`,`removeNode`,`fireEvent`,`currentStyle`,`runtimeStyle`从`HTMLElement`类型上移除 -* 属性`url`从`Event`类型上移除 -* 属性`execScript`,`navigate`,`item`从`Window`类型上移除 -* 属性`documentMode`,`parentWindow`,`createEventObject`从`Document`类型上移除 -* 属性`parentWindow`从`HTMLDocument`类型上移除 -* 属性`setCapture`被完全移除 -* 属性`releaseCapture`被完全移除 -* 属性`setAttribute`,`styleFloat`,`pixelLeft`从`CSSStyleDeclaration`类型上移除 -* 属性`selectorText`从`CSSRule`类型上移除 -* `CSSStyleSheet.rules`现在是`CSSRuleList`类型,而非`MSCSSRuleList` -* `documentElement`现在是`Element`类型,而非`HTMLElement` -* `Event`具有一个新的必需属性`returnValue` -* `Node`具有一个新的必需属性`baseURI` -* `Element`具有一个新的必需属性`classList` -* `Location`具有一个新的必需属性`origin` -* 属性`MSPOINTER_TYPE_MOUSE`,`MSPOINTER_TYPE_TOUCH`从`MSPointerEvent`类型上移除 -* `CSSStyleRule`具有一个新的必需属性`readonly` -* 属性`execUnsafeLocalFunction`从`MSApp`类型上移除 -* 全局方法`toStaticHTML`被移除 -* `HTMLCanvasElement.getContext`现在返回`CanvasRenderingContext2D | WebGLRenderingContex` -* 移除扩展类型`Dataview`,`Weakmap`,`Map`,`Set` -* `XMLHttpRequest.send`具有两个重载`send(data?: Document): void;`和`send(data?: String): void;` -* `window.orientation`现在是`string`类型,而非`number` -* 特定于IE的`attachEvent`和`detachEvent`从`Window`上移除 - -**以下是被新加的DOM类型所部分或全部取代的代码库的代表:** - -* `DefinitelyTyped/auth0/auth0.d.ts` -* `DefinitelyTyped/gamepad/gamepad.d.ts` -* `DefinitelyTyped/interactjs/interact.d.ts` -* `DefinitelyTyped/webaudioapi/waa.d.ts` -* `DefinitelyTyped/webcrypto/WebCrypto.d.ts` +- 属性`selection`从`Document`类型上移除 +- 属性`clipboardData`从`Window`类型上移除 +- 删除接口`MSEventAttachmentTarget` +- 属性`onresize`,`disabled`,`uniqueID`,`removeNode`,`fireEvent`,`currentStyle`,`runtimeStyle`从`HTMLElement`类型上移除 +- 属性`url`从`Event`类型上移除 +- 属性`execScript`,`navigate`,`item`从`Window`类型上移除 +- 属性`documentMode`,`parentWindow`,`createEventObject`从`Document`类型上移除 +- 属性`parentWindow`从`HTMLDocument`类型上移除 +- 属性`setCapture`被完全移除 +- 属性`releaseCapture`被完全移除 +- 属性`setAttribute`,`styleFloat`,`pixelLeft`从`CSSStyleDeclaration`类型上移除 +- 属性`selectorText`从`CSSRule`类型上移除 +- `CSSStyleSheet.rules`现在是`CSSRuleList`类型,而非`MSCSSRuleList` +- `documentElement`现在是`Element`类型,而非`HTMLElement` +- `Event`具有一个新的必需属性`returnValue` +- `Node`具有一个新的必需属性`baseURI` +- `Element`具有一个新的必需属性`classList` +- `Location`具有一个新的必需属性`origin` +- 属性`MSPOINTER_TYPE_MOUSE`,`MSPOINTER_TYPE_TOUCH`从`MSPointerEvent`类型上移除 +- `CSSStyleRule`具有一个新的必需属性`readonly` +- 属性`execUnsafeLocalFunction`从`MSApp`类型上移除 +- 全局方法`toStaticHTML`被移除 +- `HTMLCanvasElement.getContext`现在返回`CanvasRenderingContext2D | WebGLRenderingContex` +- 移除扩展类型`Dataview`,`Weakmap`,`Map`,`Set` +- `XMLHttpRequest.send`具有两个重载`send(data?: Document): void;`和`send(data?: String): void;` +- `window.orientation`现在是`string`类型,而非`number` +- 特定于 IE 的`attachEvent`和`detachEvent`从`Window`上移除 + +**以下是被新加的 DOM 类型所部分或全部取代的代码库的代表:** + +- `DefinitelyTyped/auth0/auth0.d.ts` +- `DefinitelyTyped/gamepad/gamepad.d.ts` +- `DefinitelyTyped/interactjs/interact.d.ts` +- `DefinitelyTyped/webaudioapi/waa.d.ts` +- `DefinitelyTyped/webcrypto/WebCrypto.d.ts` 更多信息,查看[完整改动](https://github.com/Microsoft/TypeScript/pull/2739)。 ## 类代码体将以严格格式解析 -按照[ES6规范](http://www.ecma-international.org/ecma-262/6.0/#sec-strict-mode-code),类代码体现在以严格模式进行解析。行为将相当于在类作用域顶端定义了`"use strict"`;它包括限制了把`arguments`和`eval`做为变量名或参数名的使用,把未来保留字做为变量或参数使用,八进制数字字面量的使用等。 - +按照[ES6 规范](http://www.ecma-international.org/ecma-262/6.0/#sec-strict-mode-code),类代码体现在以严格模式进行解析。行为将相当于在类作用域顶端定义了`"use strict"`;它包括限制了把`arguments`和`eval`做为变量名或参数名的使用,把未来保留字做为变量或参数使用,八进制数字字面量的使用等。 diff --git a/zh/breaking-changes/typescript-1.6.md b/zh/breaking-changes/typescript-1.6.md index bc4d8a6f..ed6b7948 100644 --- a/zh/breaking-changes/typescript-1.6.md +++ b/zh/breaking-changes/typescript-1.6.md @@ -12,10 +12,10 @@ ```typescript var x: { foo: number }; -x = { foo: 1, baz: 2 }; // Error, excess property `baz` +x = { foo: 1, baz: 2 }; // Error, excess property `baz` -var y: { foo: number, bar?: number }; -y = { foo: 1, baz: 2 }; // Error, excess or misspelled property `baz` +var y: { foo: number; bar?: number }; +y = { foo: 1, baz: 2 }; // Error, excess or misspelled property `baz` ``` **推荐:** @@ -25,17 +25,18 @@ y = { foo: 1, baz: 2 }; // Error, excess or misspelled property `baz` **如果目标类型接收额外的属性,可以增加一个索引:** ```typescript -var x: { foo: number, [x: string]: any }; -x = { foo: 1, baz: 2 }; // OK, `baz` matched by index signature +var x: { foo: number; [x: string]: any }; +x = { foo: 1, baz: 2 }; // OK, `baz` matched by index signature ``` **如果原始类型是一组相关联的类型,使用联合类型明确指定它们的类型而不是仅指定一个基本类型。** ```typescript -let animalList: (Dog | Cat | Turkey)[] = [ // use union type instead of Animal - {name: "Milo", meow: true }, - {name: "Pepper", bark: true}, - {name: "koko", gobble: true} +let animalList: (Dog | Cat | Turkey)[] = [ + // use union type instead of Animal + { name: 'Milo', meow: true }, + { name: 'Pepper', bark: true }, + { name: 'koko', gobble: true }, ]; ``` @@ -43,21 +44,21 @@ let animalList: (Dog | Cat | Turkey)[] = [ // use union type instead of Anima ```typescript interface Foo { - foo: number; + foo: number; } interface FooBar { - foo: number; - bar: number; + foo: number; + bar: number; } var y: Foo; y = { foo: 1, bar: 2 }; ``` -## CommonJS的模块解析不再假设路径为相对的 +## CommonJS 的模块解析不再假设路径为相对的 之前,对于`one.ts`和`two.ts`文件,如果它们在相同目录里,那么在`two.ts`里面导入`"one"`时是相对于`one.ts`的路径的。 -TypeScript 1.6在编译CommonJS时,`"one"`不再等同于"./one"。取而代之的是会相对于合适的`node_modules`文件夹进行查找,与Node.js在运行时解析模块相似。更多详情,阅读[the issue that describes the resolution algorithm](https://github.com/Microsoft/TypeScript/issues/2338)。 +TypeScript 1.6 在编译 CommonJS 时,`"one"`不再等同于"./one"。取而代之的是会相对于合适的`node_modules`文件夹进行查找,与 Node.js 在运行时解析模块相似。更多详情,阅读[the issue that describes the resolution algorithm](https://github.com/Microsoft/TypeScript/issues/2338)。 **例子:** @@ -65,14 +66,14 @@ TypeScript 1.6在编译CommonJS时,`"one"`不再等同于"./one"。取而代 ```typescript export function f() { - return 10; + return 10; } ``` `./two.ts` ```typescript -import { f as g } from "one"; +import { f as g } from 'one'; ``` **推荐:** @@ -83,14 +84,14 @@ import { f as g } from "one"; ```typescript export function f() { - return 10; + return 10; } ``` `./two.ts` ```typescript -import { f as g } from "./one"; +import { f as g } from './one'; ``` **将`--moduleResolution`编译器选项设置为`classic`。** @@ -100,11 +101,10 @@ import { f as g } from "./one"; 在同一空间内默认导出声明的名字与空间内一实体名相同时会得到一个错误;比如, ```typescript -export default function foo() { -} +export default function foo() {} namespace foo { - var x = 100; + var x = 100; } ``` @@ -112,11 +112,11 @@ namespace foo { ```typescript export default class Foo { - a: number; + a: number; } interface Foo { - b: string; + b: string; } ``` @@ -125,11 +125,9 @@ interface Foo { 然而,在下面的例子里合并是被允许的,因为命名空间并不具备做为值的意义: ```typescript -export default class Foo { -} +export default class Foo {} -namespace Foo { -} +namespace Foo {} ``` **推荐:** @@ -138,11 +136,11 @@ namespace Foo { ```typescript class Foo { - a: number; + a: number; } interface foo { - b: string; + b: string; } export default Foo; @@ -152,29 +150,28 @@ export default Foo; ## 模块体以严格模式解析 -按照[ES6规范](http://www.ecma-international.org/ecma-262/6.0/#sec-strict-mode-code),模块体现在以严格模式进行解析。行为将相当于在模块作用域顶端定义了`"use strict"`;它包括限制了把`arguments`和`eval`做为变量名或参数名的使用,把未来保留字做为变量或参数使用,八进制数字字面量的使用等。 +按照[ES6 规范](http://www.ecma-international.org/ecma-262/6.0/#sec-strict-mode-code),模块体现在以严格模式进行解析。行为将相当于在模块作用域顶端定义了`"use strict"`;它包括限制了把`arguments`和`eval`做为变量名或参数名的使用,把未来保留字做为变量或参数使用,八进制数字字面量的使用等。 -## 标准库里DOM API的改动 +## 标准库里 DOM API 的改动 -* **MessageEvent**和**ProgressEvent**构造函数希望传入参数;查看[issue \#4295](https://github.com/Microsoft/TypeScript/issues/4295)。 -* **ImageData**构造函数希望传入参数;查看[issue \#4220](https://github.com/Microsoft/TypeScript/issues/4220)。 -* **File**构造函数希望传入参数;查看[issue \#3999](https://github.com/Microsoft/TypeScript/issues/3999)。 +- **MessageEvent**和**ProgressEvent**构造函数希望传入参数;查看[issue \#4295](https://github.com/Microsoft/TypeScript/issues/4295)。 +- **ImageData**构造函数希望传入参数;查看[issue \#4220](https://github.com/Microsoft/TypeScript/issues/4220)。 +- **File**构造函数希望传入参数;查看[issue \#3999](https://github.com/Microsoft/TypeScript/issues/3999)。 ## 系统模块输出使用批量导出 -编译器以系统模块的格式使用新的`_export`函数[批量导出](https://github.com/ModuleLoader/es6-module-loader/issues/386)的变体,它接收任何包含键值对的对象做为参数而不是key, value。 +编译器以系统模块的格式使用新的`_export`函数[批量导出](https://github.com/ModuleLoader/es6-module-loader/issues/386)的变体,它接收任何包含键值对的对象做为参数而不是 key, value。 模块加载器需要升级到[v0.17.1](https://github.com/ModuleLoader/es6-module-loader/releases/tag/v0.17.1)或更高。 -## npm包的.js内容从'bin'移到了'lib' +## npm 包的.js 内容从'bin'移到了'lib' -TypeScript的npm包入口位置从`bin`移动到了`lib`,以防‘node\_modules/typescript/bin/typescript.js’通过IIS访问的时候造成阻塞(`bin`默认是隐藏段因此IIS会阻止访问这个文件夹)。 +TypeScript 的 npm 包入口位置从`bin`移动到了`lib`,以防‘node_modules/typescript/bin/typescript.js’通过 IIS 访问的时候造成阻塞(`bin`默认是隐藏段因此 IIS 会阻止访问这个文件夹)。 -## TypeScript的npm包不会默认全局安装 +## TypeScript 的 npm 包不会默认全局安装 -TypeScript 1.6从package.json里移除了`preferGlobal`标记。如果你依赖于这种行为,请使用`npm install -g typescript`。 +TypeScript 1.6 从 package.json 里移除了`preferGlobal`标记。如果你依赖于这种行为,请使用`npm install -g typescript`。 ## 装饰器做为调用表达式进行检查 -从1.6开始,装饰器类型检查更准确了;编译器会将装饰器表达式做为以被装饰的实体做为参数的调用表达式来进行检查。这可能会造成以前的代码报错。 - +从 1.6 开始,装饰器类型检查更准确了;编译器会将装饰器表达式做为以被装饰的实体做为参数的调用表达式来进行检查。这可能会造成以前的代码报错。 diff --git a/zh/breaking-changes/typescript-1.7.md b/zh/breaking-changes/typescript-1.7.md index 0a4aab41..3b5b22ed 100644 --- a/zh/breaking-changes/typescript-1.7.md +++ b/zh/breaking-changes/typescript-1.7.md @@ -10,14 +10,14 @@ ```typescript class Fighter { - /** @returns the winner of the fight. */ - fight(opponent: Fighter) { - let theVeryBest = this; - if (Math.rand() < 0.5) { - theVeryBest = opponent; // error - } - return theVeryBest + /** @returns the winner of the fight. */ + fight(opponent: Fighter) { + let theVeryBest = this; + if (Math.rand() < 0.5) { + theVeryBest = opponent; // error } + return theVeryBest; + } } ``` @@ -27,20 +27,20 @@ class Fighter { ```typescript class Fighter { - /** @returns the winner of the fight. */ - fight(opponent: Fighter) { - let theVeryBest: Fighter = this; - if (Math.rand() < 0.5) { - theVeryBest = opponent; // no error - } - return theVeryBest + /** @returns the winner of the fight. */ + fight(opponent: Fighter) { + let theVeryBest: Fighter = this; + if (Math.rand() < 0.5) { + theVeryBest = opponent; // no error } + return theVeryBest; + } } ``` ## 类成员修饰符后面会自动插入分号 -关键字`abstract,public,protected`和`private`是ECMAScript 3里的_保留关键字_并适用于自动插入分号机制。 之前,在这些关键字出现的行尾,TypeScript是不会插入分号的。 现在,这已经被改正了,在上例中`abstract class D`不再能够正确地继承`C`了,而是声明了一个`m`方法和一个额外的属性`abstract`。 +关键字`abstract,public,protected`和`private`是 ECMAScript 3 里的*保留关键字*并适用于自动插入分号机制。 之前,在这些关键字出现的行尾,TypeScript 是不会插入分号的。 现在,这已经被改正了,在上例中`abstract class D`不再能够正确地继承`C`了,而是声明了一个`m`方法和一个额外的属性`abstract`。 注意,`async`和`declare`已经能够正确自动插入分号了。 @@ -48,15 +48,14 @@ class Fighter { ```typescript abstract class C { - abstract m(): number; + abstract m(): number; } abstract class D extends C { - abstract - m(): number; + abstract; + m(): number; } ``` **推荐:** 在定义类成员时删除关键字后面的换行。通常来讲,要避免依赖于自动插入分号机制。 - diff --git a/zh/breaking-changes/typescript-1.8.md b/zh/breaking-changes/typescript-1.8.md index 32be263f..036adbbf 100644 --- a/zh/breaking-changes/typescript-1.8.md +++ b/zh/breaking-changes/typescript-1.8.md @@ -128,4 +128,3 @@ class D extends B { } } ``` - diff --git a/zh/breaking-changes/typescript-2.0.md b/zh/breaking-changes/typescript-2.0.md index a87f3c14..1cdb4dae 100644 --- a/zh/breaking-changes/typescript-2.0.md +++ b/zh/breaking-changes/typescript-2.0.md @@ -4,27 +4,27 @@ ## 对函数或类表达式的捕获变量不进行类型细化\(narrowing\) -类型细化不会在函数,类和lambda表达式上进行。 +类型细化不会在函数,类和 lambda 表达式上进行。 **例子** ```typescript var x: number | string; -if (typeof x === "number") { - function inner(): number { - return x; // Error, type of x is not narrowed, c is number | string - } - var y: number = x; // OK, x is number +if (typeof x === 'number') { + function inner(): number { + return x; // Error, type of x is not narrowed, c is number | string + } + var y: number = x; // OK, x is number } ``` 编译器不知道回调函数什么时候被执行。考虑下面的情况: ```typescript -var x: number | string = "a"; -if (typeof x === "string") { - setTimeout(() => console.log(x.charAt(0)), 0); +var x: number | string = 'a'; +if (typeof x === 'string') { + setTimeout(() => console.log(x.charAt(0)), 0); } x = 5; ``` @@ -36,9 +36,9 @@ x = 5; 使用常量代替: ```typescript -const x: number | string = "a"; -if (typeof x === "string") { - setTimeout(() => console.log(x.charAt(0)), 0); +const x: number | string = 'a'; +if (typeof x === 'string') { + setTimeout(() => console.log(x.charAt(0)), 0); } ``` @@ -48,22 +48,24 @@ if (typeof x === "string") { ```typescript function g(obj: T) { - var t: T; - if (obj instanceof RegExp) { - t = obj; // RegExp is not assignable to T - } + var t: T; + if (obj instanceof RegExp) { + t = obj; // RegExp is not assignable to T + } } ``` **推荐** 可以把局部变量声明为特定类型而不是泛型参数或者使用类型断言。 -## 只有get而没有set的存取器会被自动推断为`readonly`属性 +## 只有 get 而没有 set 的存取器会被自动推断为`readonly`属性 **例子** ```typescript class C { - get x() { return 0; } + get x() { + return 0; + } } var c = new C(); @@ -72,17 +74,17 @@ c.x = 1; // Error Left-hand side is a readonly property **推荐** -定义一个不对属性写值的setter。 +定义一个不对属性写值的 setter。 ## 在严格模式下函数声明不允许出现在块\(block\)里 -在严格模式下这已经是一个运行时错误。从TypeScript 2.0开始,它会被标记为编译时错误。 +在严格模式下这已经是一个运行时错误。从 TypeScript 2.0 开始,它会被标记为编译时错误。 **例子** ```typescript -if( true ) { - function foo() {} +if (true) { + function foo() {} } export = foo; @@ -93,26 +95,25 @@ export = foo; 使用函数表达式代替: ```typescript -if( true ) { - const foo = function() {} +if (true) { + const foo = function () {}; } ``` ## `TemplateStringsArray`现是是不可变的 -ES2015模版字符串总是将它们的标签以不可变的类数组对象进行传递,这个对象带有一个`raw`属性(同样是不可变的)。 TypeScript把这个对象命名为`TemplateStringsArray`。 +ES2015 模版字符串总是将它们的标签以不可变的类数组对象进行传递,这个对象带有一个`raw`属性(同样是不可变的)。 TypeScript 把这个对象命名为`TemplateStringsArray`。 便利的是,`TemplateStringsArray`可以赋值给`Array`,因此你可以利用这个较短的类型来使用标签参数: ```typescript function myTemplateTag(strs: string[]) { - // ... + // ... } ``` -然而,在TypeScript 2.0,支持用`readonly`修饰符表示这些对象是不可变的。 这样的话,`TemplateStringsArray` 就变成了不可变的,并且不再可以赋值给`string[]`。 +然而,在 TypeScript 2.0,支持用`readonly`修饰符表示这些对象是不可变的。 这样的话,`TemplateStringsArray` 就变成了不可变的,并且不再可以赋值给`string[]`。 **推荐** 直接使用`TemplateStringsArray`(或者使用`ReadonlyArray`)。 - diff --git a/zh/breaking-changes/typescript-2.1.md b/zh/breaking-changes/typescript-2.1.md index 1aff79d5..6a00cc10 100644 --- a/zh/breaking-changes/typescript-2.1.md +++ b/zh/breaking-changes/typescript-2.1.md @@ -4,7 +4,7 @@ ## 生成的构造函数代码将`this`的值替换为`super(...)`调用的返回值 -在ES2015中,如果构造函数返回一个对象,那么对于任何`super(...)`的调用者将隐式地替换掉`this`的值。 因此,有必要获取任何可能的`super(...)`的返回值并用`this`进行替换。 +在 ES2015 中,如果构造函数返回一个对象,那么对于任何`super(...)`的调用者将隐式地替换掉`this`的值。 因此,有必要获取任何可能的`super(...)`的返回值并用`this`进行替换。 **示例** @@ -12,11 +12,11 @@ ```typescript class C extends B { - public a: number; - constructor() { - super(); - this.a = 0; - } + public a: number; + constructor() { + super(); + this.a = 0; + } } ``` @@ -24,27 +24,27 @@ class C extends B { ```javascript var C = (function (_super) { - __extends(C, _super); - function C() { - var _this = _super.call(this) || this; - _this.a = 0; - return _this; - } - return C; -}(B)); + __extends(C, _super); + function C() { + var _this = _super.call(this) || this; + _this.a = 0; + return _this; + } + return C; +})(B); ``` 注意: -* `_super.call(this)`存入局部变量`_this` -* 构造函数体里所有使用`this`的地方都被替换为`super`调用的返回值(例如`_this`) -* 每个构造函数将明确地返回它的`this`,以确保正确的继承 +- `_super.call(this)`存入局部变量`_this` +- 构造函数体里所有使用`this`的地方都被替换为`super`调用的返回值(例如`_this`) +- 每个构造函数将明确地返回它的`this`,以确保正确的继承 值得注意的是在`super(...)`调用前就使用`this`从[TypeScript 1.8](typescript-2.1.md#disallow-this-accessing-before-super-call)开始将会引发错误。 ## 继承内置类型如`Error`,`Array`和`Map`将是无效的 -做为将`this`的值替换为`super(...)`调用返回值的一部分,子类化`Error`,`Array`等的结果可以是非预料的。 这是因为`Error`,`Array`等的构造函数会使用ECMAScript 6的`new.target`来调整它们的原型链; 然而,在ECMAScript 5中调用构造函数时却没有有效的方法来确保`new.target`的值。 在默认情况下,其它低级别的编译器也普遍存在这个限制。 +做为将`this`的值替换为`super(...)`调用返回值的一部分,子类化`Error`,`Array`等的结果可以是非预料的。 这是因为`Error`,`Array`等的构造函数会使用 ECMAScript 6 的`new.target`来调整它们的原型链; 然而,在 ECMAScript 5 中调用构造函数时却没有有效的方法来确保`new.target`的值。 在默认情况下,其它低级别的编译器也普遍存在这个限制。 **示例** @@ -52,19 +52,19 @@ var C = (function (_super) { ```typescript class FooError extends Error { - constructor(m: string) { - super(m); - } - sayHello() { - return "hello " + this.message; - } + constructor(m: string) { + super(m); + } + sayHello() { + return 'hello ' + this.message; + } } ``` 你会发现: -* 由这个子类构造出来的对象上的方法可能为`undefined`,因此调用`sayHello`会引发错误。 -* `instanceof`应用于子类与其实例之前会失效,因此`(new FooError()) instanceof FooError`会返回`false`。 +- 由这个子类构造出来的对象上的方法可能为`undefined`,因此调用`sayHello`会引发错误。 +- `instanceof`应用于子类与其实例之前会失效,因此`(new FooError()) instanceof FooError`会返回`false`。 **推荐** @@ -72,22 +72,22 @@ class FooError extends Error { ```typescript class FooError extends Error { - constructor(m: string) { - super(m); + constructor(m: string) { + super(m); - // Set the prototype explicitly. - Object.setPrototypeOf(this, FooError.prototype); - } + // Set the prototype explicitly. + Object.setPrototypeOf(this, FooError.prototype); + } - sayHello() { - return "hello " + this.message; - } + sayHello() { + return 'hello ' + this.message; + } } ``` 但是,任何`FooError`的子类也必须要手动地设置原型。 对于那些不支持[`Object.setPrototypeOf`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf)的运行时环境,你可以使用[`__proto__`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto)。 -不幸的是,\[这些变通方法在IE10及其之前的版本\]\([https://msdn.microsoft.com/en-us/library/s4esdbwz\(v=vs.94\).aspx](https://msdn.microsoft.com/en-us/library/s4esdbwz%28v=vs.94%29.aspx)\) 你可以手动地将方法从原型上拷贝到实例上(比如从`FooError.prototype`到`this`),但是原型链却是无法修复的。 +不幸的是,\[这些变通方法在 IE10 及其之前的版本\]\([https://msdn.microsoft.com/en-us/library/s4esdbwz\(v=vs.94\).aspx](https://msdn.microsoft.com/en-us/library/s4esdbwz%28v=vs.94%29.aspx)\) 你可以手动地将方法从原型上拷贝到实例上(比如从`FooError.prototype`到`this`),但是原型链却是无法修复的。 ## `const`变量和`readonly`属性会默认地推断成字面类型 @@ -120,7 +120,7 @@ const DEBUG = true; // `boolean`类型 ```typescript declare function push(...args: T[]): T; -var x = push("A", "B", "C"); // 推断成 "A" | "B" | "C" 在TS 2.1, 在TS 2.0里为 string +var x = push('A', 'B', 'C'); // 推断成 "A" | "B" | "C" 在TS 2.1, 在TS 2.0里为 string ``` **推荐** @@ -128,12 +128,12 @@ var x = push("A", "B", "C"); // 推断成 "A" | "B" | "C" 在TS 2.1, 在TS 2.0 在调用处明确指定参数类型: ```typescript -var x = push("A", "B", "C"); // x是string +var x = push('A', 'B', 'C'); // x是string ``` -## 没有注解的callback参数如果没有与之匹配的重载参数会触发implicit-any错误 +## 没有注解的 callback 参数如果没有与之匹配的重载参数会触发 implicit-any 错误 -在之前编译器默默地赋予callback(下面的`c`)的参数一个`any`类型。原因关乎到编译器如何解析重载的函数表达式。从TypeScript 2.1开始,在使用`--noImplicitAny`时,这会触发一个错误。 +在之前编译器默默地赋予 callback(下面的`c`)的参数一个`any`类型。原因关乎到编译器如何解析重载的函数表达式。从 TypeScript 2.1 开始,在使用`--noImplicitAny`时,这会触发一个错误。 **示例** @@ -141,24 +141,24 @@ var x = push("A", "B", "C"); // x是string declare function func(callback: () => void): any; declare function func(callback: (arg: number) => void): any; -func(c => { }); +func(c => {}); ``` **推荐** -删除第一个重载,因为它实在没什么意义;上面的函数可以使用1个或0个必须参数调用,因为函数可以安全地忽略额外的参数。 +删除第一个重载,因为它实在没什么意义;上面的函数可以使用 1 个或 0 个必须参数调用,因为函数可以安全地忽略额外的参数。 ```typescript declare function func(callback: (arg: number) => void): any; -func(c => { }); -func(() => { }); +func(c => {}); +func(() => {}); ``` -或者,你可以给callback的参数指定一个明确的类型: +或者,你可以给 callback 的参数指定一个明确的类型: ```typescript -func((c:number) => { }); +func((c: number) => {}); ``` ## 逗号操作符使用在无副作用的表达式里时会被标记成错误 @@ -172,8 +172,8 @@ let x = Math.pow((3, 5)); // x = NaN, was meant to be `Math.pow(3, 5)` // This code does not do what it appears to! let arr = []; -switch(arr.length) { - case 0, 1: +switch (arr.length) { + case (0, 1): return 'zero or one'; default: return 'more than one'; @@ -189,11 +189,10 @@ let a = 0; let y = (void a, 1); // no warning for `a` ``` -## 标准库里的DOM API变动 +## 标准库里的 DOM API 变动 -* **Node.firstChild**,**Node.lastChild**,**Node.nextSibling**,**Node.previousSibling**,**Node.parentElement**和**Node.parentNode**现在是`Node | null`而非`Node`。 +- **Node.firstChild**,**Node.lastChild**,**Node.nextSibling**,**Node.previousSibling**,**Node.parentElement**和**Node.parentNode**现在是`Node | null`而非`Node`。 查看[\#11113](https://github.com/Microsoft/TypeScript/issues/11113)了解详细信息。 推荐明确检查`null`或使用`!`断言操作符(比如`node.lastChild!`)。 - diff --git a/zh/breaking-changes/typescript-2.2.md b/zh/breaking-changes/typescript-2.2.md index 75671b3a..3369fa6b 100644 --- a/zh/breaking-changes/typescript-2.2.md +++ b/zh/breaking-changes/typescript-2.2.md @@ -2,8 +2,7 @@ 完整的破坏性改动列表请到这里查看:[breaking change issues](https://github.com/Microsoft/TypeScript/issues?q=is%3Aissue+milestone%3A%22TypeScript+2.2%22+label%3A%22Breaking+Change%22+is%3Aclosed). -## 标准库里的DOM API变动 - -* 现在标准库里有`Window.fetch`的声明;仍依赖于`@types\whatwg-fetch`会产生声明冲突错误,需要被移除。 -* 现在标准库里有`ServiceWorker`的声明;仍依赖于`@types\service_worker_api`会产生声明冲突错误,需要被移除。 +## 标准库里的 DOM API 变动 +- 现在标准库里有`Window.fetch`的声明;仍依赖于`@types\whatwg-fetch`会产生声明冲突错误,需要被移除。 +- 现在标准库里有`ServiceWorker`的声明;仍依赖于`@types\service_worker_api`会产生声明冲突错误,需要被移除。 diff --git a/zh/breaking-changes/typescript-2.3.md b/zh/breaking-changes/typescript-2.3.md index 3f306ed8..524ce71b 100644 --- a/zh/breaking-changes/typescript-2.3.md +++ b/zh/breaking-changes/typescript-2.3.md @@ -7,8 +7,7 @@ **示例** ```typescript -class X<> {} // Error: Type parameter list cannot be empty. -function f<>() {} // Error: Type parameter list cannot be empty. -const x: X<> = new X<>(); // Error: Type parameter list cannot be empty. +class X<> {} // Error: Type parameter list cannot be empty. +function f<>() {} // Error: Type parameter list cannot be empty. +const x: X<> = new X<>(); // Error: Type parameter list cannot be empty. ``` - diff --git a/zh/breaking-changes/typescript-2.4.md b/zh/breaking-changes/typescript-2.4.md index ed1a69ad..fb2e57b6 100644 --- a/zh/breaking-changes/typescript-2.4.md +++ b/zh/breaking-changes/typescript-2.4.md @@ -4,13 +4,13 @@ ## 弱类型检测 -TypeScript 2.4引入了“弱类型(weak type)”的概念。 若一个类型只包含可选的属性,那么它就被认为是_弱(weak)_的。 例如,下面的`Options`类型就是一个弱类型: +TypeScript 2.4 引入了“弱类型(weak type)”的概念。 若一个类型只包含可选的属性,那么它就被认为是*弱(weak)*的。 例如,下面的`Options`类型就是一个弱类型: ```typescript interface Options { - data?: string, - timeout?: number, - maxRetries?: number, + data?: string; + timeout?: number; + maxRetries?: number; } ``` @@ -18,13 +18,13 @@ TypeScript 2.4,当给一个弱类型赋值,但是它们之前没有共同的 ```typescript function sendMessage(options: Options) { - // ... + // ... } const opts = { - payload: "hello world!", - retryOnFail: true, -} + payload: 'hello world!', + retryOnFail: true, +}; // 错误! sendMessage(opts); @@ -40,22 +40,22 @@ sendMessage(opts); ## 推断返回值的类型 -TypeScript现在可从上下文类型中推断出一个调用的返回值类型。 这意味着一些代码现在会适当地报错。 下面是一个例子: +TypeScript 现在可从上下文类型中推断出一个调用的返回值类型。 这意味着一些代码现在会适当地报错。 下面是一个例子: ```typescript let x: Promise = new Promise(resolve => { - resolve(10); - // ~~ 错误! 'number'类型不能赋值给'string'类型 + resolve(10); + // ~~ 错误! 'number'类型不能赋值给'string'类型 }); ``` ## 更严格的回调函数参数变化 -TypeScript对回调函数参数的检测将与立即签名检测协变。 之前是双变的,这会导致有时候错误的类型也能通过检测。 根本上讲,这意味着回调函数参数和包含回调的类会被更细致地检查,因此Typescript会要求更严格的类型。 这在Promises和Observables上是十分明显的。 +TypeScript 对回调函数参数的检测将与立即签名检测协变。 之前是双变的,这会导致有时候错误的类型也能通过检测。 根本上讲,这意味着回调函数参数和包含回调的类会被更细致地检查,因此 Typescript 会要求更严格的类型。 这在 Promises 和 Observables 上是十分明显的。 ### Promises -下面是改进后的Promise检查的例子: +下面是改进后的 Promise 检查的例子: ```typescript let p = new Promise((c, e) => { c(12) }); @@ -64,19 +64,23 @@ let u: Promise = p; 类型 'Promise<{}>' 不能赋值给 'Promise' ``` -TypeScript无法在调用`new Promise`时推断类型参数`T`的值。 因此,它仅推断为`Promise<{}>`。 不幸的是,它会允许你这样写`c(12)`和`c('foo')`,就算`p`的声明明确指出它应该是`Promise`。 +TypeScript 无法在调用`new Promise`时推断类型参数`T`的值。 因此,它仅推断为`Promise<{}>`。 不幸的是,它会允许你这样写`c(12)`和`c('foo')`,就算`p`的声明明确指出它应该是`Promise`。 -在新的规则下,`Promise<{}>`不能够赋值给`Promise`,因为它破坏了Promise的回调函数。 TypeScript仍无法推断类型参数,所以你只能通过传递类型参数来解决这个问题: +在新的规则下,`Promise<{}>`不能够赋值给`Promise`,因为它破坏了 Promise 的回调函数。 TypeScript 仍无法推断类型参数,所以你只能通过传递类型参数来解决这个问题: ```typescript -let p: Promise = new Promise((c, e) => { c(12) }); +let p: Promise = new Promise((c, e) => { + c(12); +}); // ^^^^^^^^ 明确的类型参数 ``` -它能够帮助从promise代码体里发现错误。 现在,如果你错误地调用`c('foo')`,你就会得到一个错误提示: +它能够帮助从 promise 代码体里发现错误。 现在,如果你错误地调用`c('foo')`,你就会得到一个错误提示: ```typescript -let p: Promise = new Promise((c, e) => { c('foo') }); +let p: Promise = new Promise((c, e) => { + c('foo'); +}); // ~~~~~ // 参数类型 '"foo"' 不能赋值给 'number' ``` @@ -98,20 +102,20 @@ f((nested: (error: number) => void) => { log(error) }); 修复这个问题很容易。给嵌套的回调传入缺失的参数: ```typescript -f((nested: (error: number, result: any) => void) => { }); +f((nested: (error: number, result: any) => void) => {}); ``` ## 更严格的泛型函数检查 -TypeScript在比较两个单一签名的类型时会尝试统一类型参数。 结果就是,当关系到两个泛型签名时检查变得更严格了,但同时也会捕获一些bug。 +TypeScript 在比较两个单一签名的类型时会尝试统一类型参数。 结果就是,当关系到两个泛型签名时检查变得更严格了,但同时也会捕获一些 bug。 ```typescript type A = (x: T, y: U) => [T, U]; type B = (x: S, y: S) => [S, S]; function f(a: A, b: B) { - a = b; // Error - b = a; // Ok + a = b; // Error + b = a; // Ok } ``` @@ -121,7 +125,7 @@ function f(a: A, b: B) { ## 从上下文类型中推荐类型参数 -在TypeScript之前,下面例子中 +在 TypeScript 之前,下面例子中 ```typescript let f: (x: T) => T = y => y; @@ -136,4 +140,3 @@ let f: (x: T) => T = y => y() + y.foo.bar; **推荐做法:** 适当地重新审视你的泛型是否为正确的约束。实在不行,就为参数加上`any`注解。 - diff --git a/zh/breaking-changes/typescript-2.6.md b/zh/breaking-changes/typescript-2.6.md index 4b4f81fe..187b67d6 100644 --- a/zh/breaking-changes/typescript-2.6.md +++ b/zh/breaking-changes/typescript-2.6.md @@ -8,24 +8,24 @@ ```typescript function f(n: number) { - n = 0; + n = 0; } class C { - private m: number; - constructor() { - this.m = 0; - } + private m: number; + constructor() { + this.m = 0; + } } ``` -现在,当启用`--noUnusedLocals`和`--noUnusedParameters`[编译器选项](https://www.typescriptlang.org/docs/handbook/compiler-options.html)时,`n`和`m`都将被标记为未使用,因为它们的值永远不会被_读_ 。以前TypeScript只会检查它们的值是否被_引用_。 +现在,当启用`--noUnusedLocals`和`--noUnusedParameters`[编译器选项](https://www.typescriptlang.org/docs/handbook/compiler-options.html)时,`n`和`m`都将被标记为未使用,因为它们的值永远不会被*读* 。以前 TypeScript 只会检查它们的值是否被*引用*。 此外,仅在其自己的实体中调用的递归函数被视为未使用。 ```typescript function f() { - f(); // Error: 'f' is declared but its value is never read + f(); // Error: 'f' is declared but its value is never read } ``` @@ -34,19 +34,18 @@ function f() { 以前,像这样的结构 ```typescript -declare module "foo" { - export default "some" + "string"; +declare module 'foo' { + export default 'some' + 'string'; } ``` -在环境上下文中未被标记为错误。声明文件和环境模块中通常禁止使用表达式,因为`typeof`之类的意图不明确,因此这与我们在这些上下文中的其他地方处理可执行代码不一致。现在,任何不是标识符或限定名称的内容都会被标记为错误。为具有上述值形状的模块制作DTS的正确方法如下: +在环境上下文中未被标记为错误。声明文件和环境模块中通常禁止使用表达式,因为`typeof`之类的意图不明确,因此这与我们在这些上下文中的其他地方处理可执行代码不一致。现在,任何不是标识符或限定名称的内容都会被标记为错误。为具有上述值形状的模块制作 DTS 的正确方法如下: ```typescript -declare module "foo" { - const _default: string; - export default _default; +declare module 'foo' { + const _default: string; + export default _default; } ``` 编译器已经生成了这样的定义,因此这只应该是手工编写的定义的问题。 - diff --git a/zh/breaking-changes/typescript-2.7.md b/zh/breaking-changes/typescript-2.7.md index 41ca733f..3842f4e0 100644 --- a/zh/breaking-changes/typescript-2.7.md +++ b/zh/breaking-changes/typescript-2.7.md @@ -12,7 +12,7 @@ var triple: [number, number, number] = [1, 2, 3]; pair = triple; ``` -但是,这_是_一个错误: +但是,这*是*一个错误: ```typescript triple = pair; @@ -29,7 +29,7 @@ for (const n of numbers) { } ``` -对此最好的解决方法是创建扩展Array的自己的类型: +对此最好的解决方法是创建扩展 Array 的自己的类型: ```typescript interface Struct extends Array { @@ -42,9 +42,9 @@ for (const n of numbers) { } ``` -## 在`allowSyntheticDefaultImports`下,对于TS和JS文件来说默认导入的类型合成不常见 +## 在`allowSyntheticDefaultImports`下,对于 TS 和 JS 文件来说默认导入的类型合成不常见 -在过去,我们在类型系统中合成一个默认导入,用于TS或JS文件,如下所示: +在过去,我们在类型系统中合成一个默认导入,用于 TS 或 JS 文件,如下所示: ```typescript export const foo = 12; @@ -54,31 +54,30 @@ export const foo = 12; ## 更严格地检查索引访问泛型类型约束 -以前,仅当类型具有索引签名时才计算索引访问类型的约束,否则它是`any`。这样就可以取消选中无效赋值。在TS 2.7.1中,编译器在这里有点聪明,并且会将约束计算为此处所有可能属性的并集。 +以前,仅当类型具有索引签名时才计算索引访问类型的约束,否则它是`any`。这样就可以取消选中无效赋值。在 TS 2.7.1 中,编译器在这里有点聪明,并且会将约束计算为此处所有可能属性的并集。 ```typescript interface O { - foo?: string; + foo?: string; } function fails(o: O, k: K) { - var s: string = o[k]; // Previously allowed, now an error - // string | undefined is not assignable to a string + var s: string = o[k]; // Previously allowed, now an error + // string | undefined is not assignable to a string } ``` ## `in`表达式被视为类型保护 -对于`n in x`表达式,其中`n`是字符串文字或字符串文字类型而`x`是联合类型,"true"分支缩小为具有可选或必需属性`n`的类型,并且 "false"分支缩小为具有可选或缺少属性`n`的类型。 如果声明类型始终具有属性`n`,则可能导致在false分支中将变量的类型缩小为`never`的情况。 +对于`n in x`表达式,其中`n`是字符串文字或字符串文字类型而`x`是联合类型,"true"分支缩小为具有可选或必需属性`n`的类型,并且 "false"分支缩小为具有可选或缺少属性`n`的类型。 如果声明类型始终具有属性`n`,则可能导致在 false 分支中将变量的类型缩小为`never`的情况。 ```typescript var x: { foo: number }; -if ("foo" in x) { - x; // { foo: number } -} -else { - x; // never +if ('foo' in x) { + x; // { foo: number } +} else { + x; // never } ``` @@ -87,12 +86,10 @@ else { 以前在结构上相同的类在条件或`||`运算符中被简化为最佳公共类型。现在这些类以联合类型维护,以便更准确地检查`instanceof`运算符。 ```typescript -class Animal { - -} +class Animal {} class Dog { - park() { } + park() {} } var a = Math.random() ? new Animal() : new Dog(); @@ -104,14 +101,11 @@ var a = Math.random() ? new Animal() : new Dog(); `CustomEvent`现在有一个`details`属性类型的类型参数。如果要从中扩展,则需要指定其他类型参数。 ```typescript -class MyCustomEvent extends CustomEvent { -} +class MyCustomEvent extends CustomEvent {} ``` 应该成为 ```typescript -class MyCustomEvent extends CustomEvent { -} +class MyCustomEvent extends CustomEvent {} ``` - diff --git a/zh/breaking-changes/typescript-2.8.md b/zh/breaking-changes/typescript-2.8.md index 59290ab6..9d46b506 100644 --- a/zh/breaking-changes/typescript-2.8.md +++ b/zh/breaking-changes/typescript-2.8.md @@ -4,35 +4,34 @@ 根据 [\#20568](https://github.com/Microsoft/TypeScript/issues/20568),未使用的类型参数之前在`--noUnusedLocals`下报告,但现在报告在`--noUnusedParameters`下。 -## 从`lib.d.ts`中删除了一些Microsoft 专用的类型 - -从DOM定义中删除一些Microsoft 专用的类型以更好地与标准对齐。 删除的类型包括: - -* `MSApp` -* `MSAppAsyncOperation` -* `MSAppAsyncOperationEventMap` -* `MSBaseReader` -* `MSBaseReaderEventMap` -* `MSExecAtPriorityFunctionCallback` -* `MSHTMLWebViewElement` -* `MSManipulationEvent` -* `MSRangeCollection` -* `MSSiteModeEvent` -* `MSUnsafeFunctionCallback` -* `MSWebViewAsyncOperation` -* `MSWebViewAsyncOperationEventMap` -* `MSWebViewSettings` +## 从`lib.d.ts`中删除了一些 Microsoft 专用的类型 + +从 DOM 定义中删除一些 Microsoft 专用的类型以更好地与标准对齐。 删除的类型包括: + +- `MSApp` +- `MSAppAsyncOperation` +- `MSAppAsyncOperationEventMap` +- `MSBaseReader` +- `MSBaseReaderEventMap` +- `MSExecAtPriorityFunctionCallback` +- `MSHTMLWebViewElement` +- `MSManipulationEvent` +- `MSRangeCollection` +- `MSSiteModeEvent` +- `MSUnsafeFunctionCallback` +- `MSWebViewAsyncOperation` +- `MSWebViewAsyncOperationEventMap` +- `MSWebViewSettings` ## `HTMLObjectElement`不再具有`alt`属性 -根据 [\#21386](https://github.com/Microsoft/TypeScript/issues/21386),DOM库已更新以反映WHATWG标准。 +根据 [\#21386](https://github.com/Microsoft/TypeScript/issues/21386),DOM 库已更新以反映 WHATWG 标准。 如果需要继续使用`alt`属性,请考虑通过全局范围中的接口合并重新打开`HTMLObjectElement`: ```typescript // Must be in a global .ts file or a 'declare global' block. interface HTMLObjectElement { - alt: string; + alt: string; } ``` - diff --git a/zh/breaking-changes/typescript-2.9.md b/zh/breaking-changes/typescript-2.9.md index 071bde96..cdecb52f 100644 --- a/zh/breaking-changes/typescript-2.9.md +++ b/zh/breaking-changes/typescript-2.9.md @@ -6,21 +6,21 @@ TypeScript 2.9 将索引类型泛化为包括 `number` 和 `symbol` 命名属性 ```typescript function useKey(o: T, k: K) { - var name: string = k; // 错误: keyof T 不能分配给 `string` + var name: string = k; // 错误: keyof T 不能分配给 `string` } ``` ### 建议 -* 如果你的函数只能处理名字符串属性的键,请在声明中使用 `Extract`: +- 如果你的函数只能处理名字符串属性的键,请在声明中使用 `Extract`: ```typescript function useKey>(o: T, k: K) { - var name: string = k; // OK + var name: string = k; // OK } ``` -* 如果你的函数可以处理所有属性键,那么更改应该是顺畅的: +- 如果你的函数可以处理所有属性键,那么更改应该是顺畅的: ```typescript function useKey(o: T, k: K) { @@ -28,17 +28,16 @@ function useKey(o: T, k: K) { } ``` -* 除此之外,还可以使用 `--keyofStringsOnly` 编译器选项禁用新行为。 +- 除此之外,还可以使用 `--keyofStringsOnly` 编译器选项禁用新行为。 ## 剩余参数后面不允许尾后逗号 以下代码是一个自 [\#22262](https://github.com/Microsoft/TypeScript/pull/22262) 开始的编译器错误: ```typescript -function f( - a: number, - ...b: number[], // 违规的尾随逗号 -) {} +function f(a: number, ...b: number[]) { + // 违规的尾随逗号 +} ``` 剩余参数上的尾随逗号不是有效的 JavaScript,并且,这个语法现在在 TypeScript 中也是一个错误。 @@ -57,5 +56,4 @@ function f(x: T) { ## 参考 -* [原文](https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#typescript-29) - +- [原文](https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#typescript-29) diff --git a/zh/breaking-changes/typescript-3.0.md b/zh/breaking-changes/typescript-3.0.md index 2d24e703..2fec73ef 100644 --- a/zh/breaking-changes/typescript-3.0.md +++ b/zh/breaking-changes/typescript-3.0.md @@ -9,7 +9,7 @@ 关闭 `strictNullChecks` 时,下例中 `A` 的类型为 `null`,而 `B` 的类型为 `undefined`: ```typescript -type A = { a: number } & null; // null +type A = { a: number } & null; // null type B = { a: number } & undefined; // undefined ``` @@ -21,5 +21,4 @@ type B = { a: number } & undefined; // undefined ## 参考 -* [原文](https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#typescript-30) - +- [原文](https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#typescript-30) diff --git a/zh/breaking-changes/typescript-3.1.md b/zh/breaking-changes/typescript-3.1.md index 279ced2c..7aad9a0d 100644 --- a/zh/breaking-changes/typescript-3.1.md +++ b/zh/breaking-changes/typescript-3.1.md @@ -2,21 +2,173 @@ ## 一些浏览器厂商特定的类型从`lib.d.ts`中被移除 -TypeScript内置的`.d.ts`库\(`lib.d.ts`等\)现在会部分地从DOM规范的Web IDL文件中生成。 因此有一些浏览器厂商特定的类型被移除了。 +TypeScript 内置的`.d.ts`库\(`lib.d.ts`等\)现在会部分地从 DOM 规范的 Web IDL 文件中生成。 因此有一些浏览器厂商特定的类型被移除了。 点击这里查看被移除类型的完整列表: - \* \`CanvasRenderingContext2D.mozImageSmoothingEnabled\` \* \`CanvasRenderingContext2D.msFillRule\` \* \`CanvasRenderingContext2D.oImageSmoothingEnabled\` \* \`CanvasRenderingContext2D.webkitImageSmoothingEnabled\` \* \`Document.caretRangeFromPoint\` \* \`Document.createExpression\` \* \`Document.createNSResolver\` \* \`Document.execCommandShowHelp\` \* \`Document.exitFullscreen\` \* \`Document.exitPointerLock\` \* \`Document.focus\` \* \`Document.fullscreenElement\` \* \`Document.fullscreenEnabled\` \* \`Document.getSelection\` \* \`Document.msCapsLockWarningOff\` \* \`Document.msCSSOMElementFloatMetrics\` \* \`Document.msElementsFromRect\` \* \`Document.msElementsFromPoint\` \* \`Document.onvisibilitychange\` \* \`Document.onwebkitfullscreenchange\` \* \`Document.onwebkitfullscreenerror\` \* \`Document.pointerLockElement\` \* \`Document.queryCommandIndeterm\` \* \`Document.URLUnencoded\` \* \`Document.webkitCurrentFullScreenElement\` \* \`Document.webkitFullscreenElement\` \* \`Document.webkitFullscreenEnabled\` \* \`Document.webkitIsFullScreen\` \* \`Document.xmlEncoding\` \* \`Document.xmlStandalone\` \* \`Document.xmlVersion\` \* \`DocumentType.entities\` \* \`DocumentType.internalSubset\` \* \`DocumentType.notations\` \* \`DOML2DeprecatedSizeProperty\` \* \`Element.msContentZoomFactor\` \* \`Element.msGetUntransformedBounds\` \* \`Element.msMatchesSelector\` \* \`Element.msRegionOverflow\` \* \`Element.msReleasePointerCapture\` \* \`Element.msSetPointerCapture\` \* \`Element.msZoomTo\` \* \`Element.onwebkitfullscreenchange\` \* \`Element.onwebkitfullscreenerror\` \* \`Element.webkitRequestFullScreen\` \* \`Element.webkitRequestFullscreen\` \* \`ElementCSSInlineStyle\` \* \`ExtendableEventInit\` \* \`ExtendableMessageEventInit\` \* \`FetchEventInit\` \* \`GenerateAssertionCallback\` \* \`HTMLAnchorElement.Methods\` \* \`HTMLAnchorElement.mimeType\` \* \`HTMLAnchorElement.nameProp\` \* \`HTMLAnchorElement.protocolLong\` \* \`HTMLAnchorElement.urn\` \* \`HTMLAreasCollection\` \* \`HTMLHeadElement.profile\` \* \`HTMLImageElement.msGetAsCastingSource\` \* \`HTMLImageElement.msGetAsCastingSource\` \* \`HTMLImageElement.msKeySystem\` \* \`HTMLImageElement.msPlayToDisabled\` \* \`HTMLImageElement.msPlayToDisabled\` \* \`HTMLImageElement.msPlayToPreferredSourceUri\` \* \`HTMLImageElement.msPlayToPreferredSourceUri\` \* \`HTMLImageElement.msPlayToPrimary\` \* \`HTMLImageElement.msPlayToPrimary\` \* \`HTMLImageElement.msPlayToSource\` \* \`HTMLImageElement.msPlayToSource\` \* \`HTMLImageElement.x\` \* \`HTMLImageElement.y\` \* \`HTMLInputElement.webkitdirectory\` \* \`HTMLLinkElement.import\` \* \`HTMLMetaElement.charset\` \* \`HTMLMetaElement.url\` \* \`HTMLSourceElement.msKeySystem\` \* \`HTMLStyleElement.disabled\` \* \`HTMLSummaryElement\` \* \`MediaQueryListListener\` \* \`MSAccountInfo\` \* \`MSAudioLocalClientEvent\` \* \`MSAudioLocalClientEvent\` \* \`MSAudioRecvPayload\` \* \`MSAudioRecvSignal\` \* \`MSAudioSendPayload\` \* \`MSAudioSendSignal\` \* \`MSConnectivity\` \* \`MSCredentialFilter\` \* \`MSCredentialParameters\` \* \`MSCredentials\` \* \`MSCredentialSpec\` \* \`MSDCCEvent\` \* \`MSDCCEventInit\` \* \`MSDelay\` \* \`MSDescription\` \* \`MSDSHEvent\` \* \`MSDSHEventInit\` \* \`MSFIDOCredentialParameters\` \* \`MSIceAddrType\` \* \`MSIceType\` \* \`MSIceWarningFlags\` \* \`MSInboundPayload\` \* \`MSIPAddressInfo\` \* \`MSJitter\` \* \`MSLocalClientEvent\` \* \`MSLocalClientEventBase\` \* \`MSNetwork\` \* \`MSNetworkConnectivityInfo\` \* \`MSNetworkInterfaceType\` \* \`MSOutboundNetwork\` \* \`MSOutboundPayload\` \* \`MSPacketLoss\` \* \`MSPayloadBase\` \* \`MSPortRange\` \* \`MSRelayAddress\` \* \`MSSignatureParameters\` \* \`MSStatsType\` \* \`MSStreamReader\` \* \`MSTransportDiagnosticsStats\` \* \`MSUtilization\` \* \`MSVideoPayload\` \* \`MSVideoRecvPayload\` \* \`MSVideoResolutionDistribution\` \* \`MSVideoSendPayload\` \* \`NotificationEventInit\` \* \`PushEventInit\` \* \`PushSubscriptionChangeInit\` \* \`RTCIdentityAssertionResult\` \* \`RTCIdentityProvider\` \* \`RTCIdentityProviderDetails\` \* \`RTCIdentityValidationResult\` \* \`Screen.deviceXDPI\` \* \`Screen.logicalXDPI\` \* \`SVGElement.xmlbase\` \* \`SVGGraphicsElement.farthestViewportElement\` \* \`SVGGraphicsElement.getTransformToElement\` \* \`SVGGraphicsElement.nearestViewportElement\` \* \`SVGStylable\` \* \`SVGTests.hasExtension\` \* \`SVGTests.requiredFeatures\` \* \`SyncEventInit\` \* \`ValidateAssertionCallback\` \* \`WebKitDirectoryEntry\` \* \`WebKitDirectoryReader\` \* \`WebKitEntriesCallback\` \* \`WebKitEntry\` \* \`WebKitErrorCallback\` \* \`WebKitFileCallback\` \* \`WebKitFileEntry\` \* \`WebKitFileSystem\` \* \`Window.clearImmediate\` \* \`Window.msSetImmediate\` \* \`Window.setImmediate\` +- `CanvasRenderingContext2D.mozImageSmoothingEnabled` +- `CanvasRenderingContext2D.msFillRule` +- `CanvasRenderingContext2D.oImageSmoothingEnabled` +- `CanvasRenderingContext2D.webkitImageSmoothingEnabled` +- `Document.caretRangeFromPoint` +- `Document.createExpression` +- `Document.createNSResolver` +- `Document.execCommandShowHelp` +- `Document.exitFullscreen` +- `Document.exitPointerLock` +- `Document.focus` +- `Document.fullscreenElement` +- `Document.fullscreenEnabled` +- `Document.getSelection` +- `Document.msCapsLockWarningOff` +- `Document.msCSSOMElementFloatMetrics` +- `Document.msElementsFromRect` +- `Document.msElementsFromPoint` +- `Document.onvisibilitychange` +- `Document.onwebkitfullscreenchange` +- `Document.onwebkitfullscreenerror` +- `Document.pointerLockElement` +- `Document.queryCommandIndeterm` +- `Document.URLUnencoded` +- `Document.webkitCurrentFullScreenElement` +- `Document.webkitFullscreenElement` +- `Document.webkitFullscreenEnabled` +- `Document.webkitIsFullScreen` +- `Document.xmlEncoding` +- `Document.xmlStandalone` +- `Document.xmlVersion` +- `DocumentType.entities` +- `DocumentType.internalSubset` +- `DocumentType.notations` +- `DOML2DeprecatedSizeProperty` +- `Element.msContentZoomFactor` +- `Element.msGetUntransformedBounds` +- `Element.msMatchesSelector` +- `Element.msRegionOverflow` +- `Element.msReleasePointerCapture` +- `Element.msSetPointerCapture` +- `Element.msZoomTo` +- `Element.onwebkitfullscreenchange` +- `Element.onwebkitfullscreenerror` +- `Element.webkitRequestFullScreen` +- `Element.webkitRequestFullscreen` +- `ElementCSSInlineStyle` +- `ExtendableEventInit` +- `ExtendableMessageEventInit` +- `FetchEventInit` +- `GenerateAssertionCallback` +- `HTMLAnchorElement.Methods` +- `HTMLAnchorElement.mimeType` +- `HTMLAnchorElement.nameProp` +- `HTMLAnchorElement.protocolLong` +- `HTMLAnchorElement.urn` +- `HTMLAreasCollection` +- `HTMLHeadElement.profile` +- `HTMLImageElement.msGetAsCastingSource` +- `HTMLImageElement.msGetAsCastingSource` +- `HTMLImageElement.msKeySystem` +- `HTMLImageElement.msPlayToDisabled` +- `HTMLImageElement.msPlayToDisabled` +- `HTMLImageElement.msPlayToPreferredSourceUri` +- `HTMLImageElement.msPlayToPreferredSourceUri` +- `HTMLImageElement.msPlayToPrimary` +- `HTMLImageElement.msPlayToPrimary` +- `HTMLImageElement.msPlayToSource` +- `HTMLImageElement.msPlayToSource` +- `HTMLImageElement.x` +- `HTMLImageElement.y` +- `HTMLInputElement.webkitdirectory` +- `HTMLLinkElement.import` +- `HTMLMetaElement.charset` +- `HTMLMetaElement.url` +- `HTMLSourceElement.msKeySystem` +- `HTMLStyleElement.disabled` +- `HTMLSummaryElement` +- `MediaQueryListListener` +- `MSAccountInfo` +- `MSAudioLocalClientEvent` +- `MSAudioLocalClientEvent` +- `MSAudioRecvPayload` +- `MSAudioRecvSignal` +- `MSAudioSendPayload` +- `MSAudioSendSignal` +- `MSConnectivity` +- `MSCredentialFilter` +- `MSCredentialParameters` +- `MSCredentials` +- `MSCredentialSpec` +- `MSDCCEvent` +- `MSDCCEventInit` +- `MSDelay` +- `MSDescription` +- `MSDSHEvent` +- `MSDSHEventInit` +- `MSFIDOCredentialParameters` +- `MSIceAddrType` +- `MSIceType` +- `MSIceWarningFlags` +- `MSInboundPayload` +- `MSIPAddressInfo` +- `MSJitter` +- `MSLocalClientEvent` +- `MSLocalClientEventBase` +- `MSNetwork` +- `MSNetworkConnectivityInfo` +- `MSNetworkInterfaceType` +- `MSOutboundNetwork` +- `MSOutboundPayload` +- `MSPacketLoss` +- `MSPayloadBase` +- `MSPortRange` +- `MSRelayAddress` +- `MSSignatureParameters` +- `MSStatsType` +- `MSStreamReader` +- `MSTransportDiagnosticsStats` +- `MSUtilization` +- `MSVideoPayload` +- `MSVideoRecvPayload` +- `MSVideoResolutionDistribution` +- `MSVideoSendPayload` +- `NotificationEventInit` +- `PushEventInit` +- `PushSubscriptionChangeInit` +- `RTCIdentityAssertionResult` +- `RTCIdentityProvider` +- `RTCIdentityProviderDetails` +- `RTCIdentityValidationResult` +- `Screen.deviceXDPI` +- `Screen.logicalXDPI` +- `SVGElement.xmlbase` +- `SVGGraphicsElement.farthestViewportElement` +- `SVGGraphicsElement.getTransformToElement` +- `SVGGraphicsElement.nearestViewportElement` +- `SVGStylable` +- `SVGTests.hasExtension` +- `SVGTests.requiredFeatures` +- `SyncEventInit` +- `ValidateAssertionCallback` +- `WebKitDirectoryEntry` +- `WebKitDirectoryReader` +- `WebKitEntriesCallback` +- `WebKitEntry` +- `WebKitErrorCallback` +- `WebKitFileCallback` +- `WebKitFileEntry` +- `WebKitFileSystem` +- `Window.clearImmediate` +- `Window.msSetImmediate` +- `Window.setImmediate` ### 推荐: -如果你的运行时能够保证这些名称是可用的(比如一个仅针对IE的应用),那么可以在本地添加那些声明,例如: +如果你的运行时能够保证这些名称是可用的(比如一个仅针对 IE 的应用),那么可以在本地添加那些声明,例如: 对于`Element.msMatchesSelector`,在本地的`dom.ie.d.ts`文件里添加如下代码: ```typescript interface Element { - msMatchesSelector(selectors: string): boolean; + msMatchesSelector(selectors: string): boolean; } ``` @@ -24,9 +176,9 @@ interface Element { ```typescript interface Window { - clearImmediate(handle: number): void; - setImmediate(handler: (...args: any[]) => void): number; - setImmediate(handler: any, ...args: any[]): number; + clearImmediate(handle: number): void; + setImmediate(handler: (...args: any[]) => void): number; + setImmediate(handler: any, ...args: any[]): number; } ``` @@ -36,15 +188,14 @@ interface Window { ```typescript function foo(x: T | (() => string)) { - if (typeof x === "function") { - x(); -// ~~~ -// Cannot invoke an expression whose type lacks a call signature. Type '(() => string) | (T & Function)' has no compatible call signatures. - } + if (typeof x === 'function') { + x(); + // ~~~ + // Cannot invoke an expression whose type lacks a call signature. Type '(() => string) | (T & Function)' has no compatible call signatures. + } } ``` 这是因为,不同于以前的`T`会被细化掉,如今`T`会被扩展成`T & Function`。 然而,因为这个类型没有声明调用签名,类型系统无法找到通用的调用签名可以适用于`T & Function`和`() => string`。 因此,考虑使用一个更确切的类型,而不是`{}`或`Object`,并且考虑给`T`添加额外的约束条件。 - diff --git a/zh/breaking-changes/typescript-3.2.md b/zh/breaking-changes/typescript-3.2.md index de5f3d43..7829f8aa 100644 --- a/zh/breaking-changes/typescript-3.2.md +++ b/zh/breaking-changes/typescript-3.2.md @@ -14,5 +14,4 @@ ## 参考 -* [原文](https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#typescript-32) - +- [原文](https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#typescript-32) diff --git a/zh/breaking-changes/typescript-3.4.md b/zh/breaking-changes/typescript-3.4.md index 95478071..7eed40e2 100644 --- a/zh/breaking-changes/typescript-3.4.md +++ b/zh/breaking-changes/typescript-3.4.md @@ -18,13 +18,20 @@ this.whargarbl = 10; 在某些情况下,TypeScript 3.4 的推断改进可能会产生泛型的函数,而不是那些接收并返回其约束的函数(通常是 `{}`)。 ```typescript -declare function compose(f: (arg: T) => U, g: (arg: U) => V): (arg: T) => V; +declare function compose( + f: (arg: T) => U, + g: (arg: U) => V +): (arg: T) => V; -function list(x: T) { return [x]; } -function box(value: T) { return { value }; } +function list(x: T) { + return [x]; +} +function box(value: T) { + return { value }; +} let f = compose(list, box); -let x = f(100) +let x = f(100); // 在 TypeScript 3.4 中, 'x.value' 的类型为 // @@ -35,7 +42,7 @@ let x = f(100) // {}[] // // 因此,插入一个 `string` 类型是错误的 -x.value.push("hello"); +x.value.push('hello'); ``` `x` 上的显式类型注释可以清除这个错误。 @@ -46,11 +53,11 @@ TypeScript 现在使用函数调用时传入的类型(如下例中的 `then` ```typescript function isEven(prom: Promise): Promise<{ success: boolean }> { - return prom.then<{success: boolean}>((x) => { - return x % 2 === 0 ? - { success: true } : - Promise.resolve({ success: false }); - }); + return prom.then<{ success: boolean }>(x => { + return x % 2 === 0 + ? { success: true } + : Promise.resolve({ success: false }); + }); } ``` @@ -69,10 +76,10 @@ Argument of type '(x: number) => Promise<{ success: false; }> | { success: true; ```typescript function isEven(prom: Promise): Promise<{ success: boolean }> { // vvvvvvvvvvvvvvvvvv - return prom.then<{success: boolean}>((x) => { - return x % 2 === 0 ? - { success: true } : - Promise.resolve({ success: false }); + return prom.then<{ success: boolean }>(x => { + return x % 2 === 0 + ? { success: true } + : Promise.resolve({ success: false }); }); } ``` @@ -88,8 +95,12 @@ function isEven(prom: Promise): Promise<{ success: boolean }> { 这导致一个可见的重大变更,只要有类型参数的接口使用了 `keyof`(包括诸如 `Record` 之类的地方,这是涉及 `keyof K` 的类型别名)。下例就是这样一个可能的变更。 ```typescript -interface HasX { x: any } -interface HasY { y: any } +interface HasX { + x: any; +} +interface HasY { + y: any; +} declare const source: HasX | HasY; declare const properties: KeyContainer; @@ -99,7 +110,7 @@ interface KeyContainer { } function readKey(source: T, prop: KeyContainer) { - console.log(source[prop.key]) + console.log(source[prop.key]); } // 这个调用应该被拒绝,因为我们可能会这样做 @@ -111,5 +122,4 @@ readKey(source, properties); ## 参考 -* [原文](https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#typescript-34) - +- [原文](https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#typescript-34) diff --git a/zh/breaking-changes/typescript-3.5.md b/zh/breaking-changes/typescript-3.5.md index f16c5e4c..e3052bd6 100644 --- a/zh/breaking-changes/typescript-3.5.md +++ b/zh/breaking-changes/typescript-3.5.md @@ -14,4 +14,3 @@ Duplicate identifier 'Omit'. 1. 删除重复定义的并使用 `lib.d.ts` 提供的。 2. 从模块中导出定义避免全局冲突。现有的用法可以使用 `import` 直接引用项目的旧 `Omit` 类型。 - diff --git a/zh/breaking-changes/typescript-3.6.md b/zh/breaking-changes/typescript-3.6.md index b9264b9b..c7e6dd9b 100644 --- a/zh/breaking-changes/typescript-3.6.md +++ b/zh/breaking-changes/typescript-3.6.md @@ -6,8 +6,8 @@ ```typescript class C { - "constructor"() { - console.log("现在我是构造函数了。"); + constructor() { + console.log('现在我是构造函数了。'); } } ``` @@ -16,8 +16,8 @@ class C { ```typescript class D { - ["constructor"]() { - console.log("我只是一个纯粹的方法,不是构造函数!") + ['constructor']() { + console.log('我只是一个纯粹的方法,不是构造函数!'); } } ``` @@ -26,10 +26,10 @@ class D { `lib.dom.d.ts` 中移除或者修改了大量的定义。其中包括(但不仅限于)以下这些: -* 全局的 `window` 不再定义为 `Window`,它被更明确的定义 `type Window & typeof globalThis` 替代。在某些情况下,将它作为 `typeof window` 更好。 -* `GlobalFetch` 已经被移除。使用 `WindowOrWorkerGlobalScrope` 替代。 -* `Navigator` 上明确的非标准的属性已经被移除了。 -* `experimental-webgl` 上下文已经被移除了。使用 `webgl` 或 `webgl2` 替代。 +- 全局的 `window` 不再定义为 `Window`,它被更明确的定义 `type Window & typeof globalThis` 替代。在某些情况下,将它作为 `typeof window` 更好。 +- `GlobalFetch` 已经被移除。使用 `WindowOrWorkerGlobalScrope` 替代。 +- `Navigator` 上明确的非标准的属性已经被移除了。 +- `experimental-webgl` 上下文已经被移除了。使用 `webgl` 或 `webgl2` 替代。 如果你认为其中的改变已经制造了错误,[请提交一个 issue](https://github.com/Microsoft/TSJS-lib-generator/)。 @@ -63,5 +63,4 @@ while (true) { ## 参考 -* [Announcing TypeScript 3.6](https://devblogs.microsoft.com/typescript/announcing-typescript-3-6/#breaking-changes) - +- [Announcing TypeScript 3.6](https://devblogs.microsoft.com/typescript/announcing-typescript-3-6/#breaking-changes) diff --git a/zh/declaration-files/README.md b/zh/declaration-files/README.md index 0363ec10..e617194b 100644 --- a/zh/declaration-files/README.md +++ b/zh/declaration-files/README.md @@ -1,9 +1,9 @@ -- [如何书写声明文件](./README.md) - - [介绍](./introduction.md) - - [举例](./by-example.md) - - [库结构](./library-structures.md) - - [模板](./templates.md) - - [最佳实践](./do-s-and-don-ts.md) - - [深入](./deep-dive.md) - - [发布](./publishing.md) - - [使用](./consumption.md) +- [如何书写声明文件](./README.md) + - [介绍](./introduction.md) + - [举例](./by-example.md) + - [库结构](./library-structures.md) + - [模板](./templates.md) + - [最佳实践](./do-s-and-don-ts.md) + - [深入](./deep-dive.md) + - [发布](./publishing.md) + - [使用](./consumption.md) diff --git a/zh/declaration-files/by-example.md b/zh/declaration-files/by-example.md index 9490c3a9..c71aa989 100644 --- a/zh/declaration-files/by-example.md +++ b/zh/declaration-files/by-example.md @@ -6,14 +6,14 @@ 这些例子是按复杂度递增的顺序组织的。 -- [带属性的对象](#带属性的对象) -- [函数重载](#函数重载) -- [可重用类型(接口)](#可重用类型接口) -- [可重用类型(类型别名)](#可重用类型类型别名) -- [组织类型](#组织类型) -- [类](#类) -- [全局变量](#全局变量) -- [全局函数](#全局函数) +- [带属性的对象](#带属性的对象) +- [函数重载](#函数重载) +- [可重用类型(接口)](#可重用类型接口) +- [可重用类型(类型别名)](#可重用类型类型别名) +- [组织类型](#组织类型) +- [类](#类) +- [全局变量](#全局变量) +- [全局函数](#全局函数) ## 带属性的对象 @@ -37,8 +37,8 @@ _声明_ ```ts declare namespace myLib { - function makeGreeting(s: string): string; - let numberOfGreetings: number; + function makeGreeting(s: string): string; + let numberOfGreetings: number; } ``` @@ -80,8 +80,8 @@ _代码_ ```ts greet({ - greeting: 'hello world', - duration: 4000, + greeting: 'hello world', + duration: 4000, }); ``` @@ -91,9 +91,9 @@ _声明_ ```ts interface GreetingSettings { - greeting: string; - duration?: number; - color?: string; + greeting: string; + duration?: number; + color?: string; } declare function greet(setting: GreetingSettings): void; @@ -109,7 +109,7 @@ _代码_ ```ts function getGreeting() { - return 'howdy'; + return 'howdy'; } class MyGreeter extends Greeter {} @@ -149,14 +149,14 @@ _声明_ ```ts declare namespace GreetingLib { - interface LogOptions { - verbose?: boolean; - } - interface AlertOptions { - modal: boolean; - title?: string; - color?: string; - } + interface LogOptions { + verbose?: boolean; + } + interface AlertOptions { + modal: boolean; + title?: string; + color?: string; + } } ``` @@ -164,15 +164,15 @@ declare namespace GreetingLib { ```ts declare namespace GreetingLib.Options { - // Refer to via GreetingLib.Options.Log - interface Log { - verbose?: boolean; - } - interface Alert { - modal: boolean; - title?: string; - color?: string; - } + // Refer to via GreetingLib.Options.Log + interface Log { + verbose?: boolean; + } + interface Alert { + modal: boolean; + title?: string; + color?: string; + } } ``` @@ -190,9 +190,9 @@ myGreeter.greeting = 'howdy'; myGreeter.showGreeting(); class SpecialGreeter extends Greeter { - constructor() { - super('Very special greetings'); - } + constructor() { + super('Very special greetings'); + } } ``` @@ -203,10 +203,10 @@ _声明_ ```ts declare class Greeter { - constructor(greeting: string); + constructor(greeting: string); - greeting: string; - showGreeting(): void; + greeting: string; + showGreeting(): void; } ``` diff --git a/zh/declaration-files/deep-dive.md b/zh/declaration-files/deep-dive.md index a77cdad2..ba310095 100644 --- a/zh/declaration-files/deep-dive.md +++ b/zh/declaration-files/deep-dive.md @@ -4,7 +4,7 @@ 比如,你可能想要这样一个模块,可以用或不用`new`来创建不同的类型,在不同层级上暴露出不同的命名类型,且模块对象上还带有一些属性。 阅读这篇指南后,你就会了解如何编写复杂的声明文件来提供友好的 API 。 -这篇指南针对于模块(或UMD)代码库,因为它们的选择具有更高的可变性。 +这篇指南针对于模块(或 UMD)代码库,因为它们的选择具有更高的可变性。 ## 核心概念 @@ -14,11 +14,11 @@ 如果你正在阅读这篇指南,你可能已经大概了解 TypeScript 里的类型指是什么。 明确一下,*类型*通过以下方式引入: -- 类型别名声明(`type sn = number | string;`) -- 接口声明(`interface I { x: number[]; }`) -- 类声明(`class C { }`) -- 枚举声明(`enum E { A, B, C }`) -- 指向某个类型的`import`声明 +- 类型别名声明(`type sn = number | string;`) +- 接口声明(`interface I { x: number[]; }`) +- 类声明(`class C { }`) +- 枚举声明(`enum E { A, B, C }`) +- 指向某个类型的`import`声明 以上每种声明形式都会创建一个新的类型名称。 @@ -30,12 +30,12 @@ 同样地,以下方式能够创建值: -- `let`,`const`,和`var`声明 -- 包含值的`namespace`或`module`声明 -- `enum`声明 -- `class`声明 -- 指向值的`import`声明 -- `function`声明 +- `let`,`const`,和`var`声明 +- 包含值的`namespace`或`module`声明 +- `enum`声明 +- `class`声明 +- 指向值的`import`声明 +- `function`声明 ### 命名空间 @@ -72,7 +72,7 @@ export interface SomeType { 这样使用它: ```ts -import * as foo from "./foo"; +import * as foo from './foo'; let x: foo.SomeType = foo.SomeVar.a; console.log(x.count); ``` @@ -90,7 +90,7 @@ export interface Bar { 这提供了使用解构的机会: ```ts -import { Bar } from "./foo"; +import { Bar } from './foo'; let x: Bar = Bar.a; console.log(x.count); ``` @@ -192,20 +192,20 @@ type X = string; 在这个例子里,第一个代码块创建了以下名字与含义: -- 一个值`X`(因为`namespace`声明包含一个值,`Z`) -- 一个命名空间`X`(因为`namespace`声明包含一个类型,`Y`) -- 在命名空间`X`里的类型`Y` -- 在命名空间`X`里的类型`Z`(类的实例结构) -- 值`X`的一个属性值`Z`(类的构造函数) +- 一个值`X`(因为`namespace`声明包含一个值,`Z`) +- 一个命名空间`X`(因为`namespace`声明包含一个类型,`Y`) +- 在命名空间`X`里的类型`Y` +- 在命名空间`X`里的类型`Z`(类的实例结构) +- 值`X`的一个属性值`Z`(类的构造函数) 第二个代码块创建了以下名字与含义: -- 值`Y`(`number`类型),它是值`X`的一个属性 -- 一个命名空间`Z` -- 值`Z`,它是值`X`的一个属性 -- 在`X.Z`命名空间下的类型`C` -- 值`X.Z`的一个属性值`C` -- 类型`X` +- 值`Y`(`number`类型),它是值`X`的一个属性 +- 一个命名空间`Z` +- 值`Z`,它是值`X`的一个属性 +- 在`X.Z`命名空间下的类型`C` +- 值`X.Z`的一个属性值`C` +- 类型`X` ## 使用`export =`或`import` diff --git a/zh/declaration-files/do-s-and-don-ts.md b/zh/declaration-files/do-s-and-don-ts.md index a64c9b10..5d57a510 100644 --- a/zh/declaration-files/do-s-and-don-ts.md +++ b/zh/declaration-files/do-s-and-don-ts.md @@ -44,7 +44,7 @@ function reverse(s: string): string; ```ts /* 错误 */ function fn(x: () => any) { - x(); + x(); } ``` @@ -53,7 +53,7 @@ function fn(x: () => any) { ```ts /* 正确 */ function fn(x: () => void) { - x(); + x(); } ``` @@ -61,8 +61,8 @@ _原因_:使用`void`相对安全,因为它能防止不小心使用了未经 ```ts function fn(x: () => void) { - var k = x(); // oops! meant to do something else - k.doSomething(); // error, but would be OK if the return type had been 'any' + var k = x(); // oops! meant to do something else + k.doSomething(); // error, but would be OK if the return type had been 'any' } ``` @@ -73,7 +73,7 @@ function fn(x: () => void) { ```ts /* 错误 */ interface Fetcher { - getObject(done: (data: any, elapsedTime?: number) => void): void; + getObject(done: (data: any, elapsedTime?: number) => void): void; } ``` @@ -86,7 +86,7 @@ interface Fetcher { ```ts /* 正确 */ interface Fetcher { - getObject(done: (data: any, elapsedTime: number) => void): void; + getObject(done: (data: any, elapsedTime: number) => void): void; } ``` @@ -98,8 +98,8 @@ interface Fetcher { /* WRONG */ declare function beforeAll(action: () => void, timeout?: number): void; declare function beforeAll( - action: (done: DoneFn) => void, - timeout?: number + action: (done: DoneFn) => void, + timeout?: number ): void; ``` @@ -108,8 +108,8 @@ declare function beforeAll( ```ts /* 正确 */ declare function beforeAll( - action: (done: DoneFn) => void, - timeout?: number + action: (done: DoneFn) => void, + timeout?: number ): void; ``` @@ -154,9 +154,9 @@ _原因_:当解析函数调用的时候,TypeScript 会选择*匹配到的第 ```ts /* WRONG */ interface Example { - diff(one: string): number; - diff(one: string, two: string): number; - diff(one: string, two: string, three: boolean): number; + diff(one: string): number; + diff(one: string, two: string): number; + diff(one: string, two: string, three: boolean): number; } ``` @@ -165,7 +165,7 @@ interface Example { ```ts /* OK */ interface Example { - diff(one: string, two?: string, three?: boolean): number; + diff(one: string, two?: string, three?: boolean): number; } ``` @@ -193,7 +193,7 @@ fn(x.diff); var x: Example; // When written with overloads, incorrectly an error because of passing 'undefined' to 'string' // When written with optionals, correctly OK -x.diff("something", true ? undefined : "hour"); +x.diff('something', true ? undefined : 'hour'); ``` ### 使用联合类型 diff --git a/zh/declaration-files/introduction.md b/zh/declaration-files/introduction.md index f4ea442f..b7deaa21 100644 --- a/zh/declaration-files/introduction.md +++ b/zh/declaration-files/introduction.md @@ -25,13 +25,13 @@ 在[模版](./templates.md)一节里,你能找到一些声明文件,它们对于编写新的声明文件来讲会有所帮助。 如果你已经了解了库的结构,那么可以阅读相应的模版文件: -- [global-modifying-module.d.ts](templates/global-modifying-module.d.ts.md) -- [global-plugin.d.ts](templates/global-plugin.d.ts.md) -- [global.d.ts](templates/global.d.ts.md) -- [module-class.d.ts](templates/module-class.d.ts.md) -- [module-function.d.ts](templates/module-function.d.ts.md) -- [module-plugin.d.ts](templates/module-plugin.d.ts.md) -- [module.d.ts](templates/module.d.ts.md) +- [global-modifying-module.d.ts](templates/global-modifying-module.d.ts.md) +- [global-plugin.d.ts](templates/global-plugin.d.ts.md) +- [global.d.ts](templates/global.d.ts.md) +- [module-class.d.ts](templates/module-class.d.ts.md) +- [module-function.d.ts](templates/module-function.d.ts.md) +- [module-plugin.d.ts](templates/module-plugin.d.ts.md) +- [module.d.ts](templates/module.d.ts.md) ## [规范](./do-s-and-don-ts.md) diff --git a/zh/declaration-files/library-structures.md b/zh/declaration-files/library-structures.md index ba96e46f..066c2691 100644 --- a/zh/declaration-files/library-structures.md +++ b/zh/declaration-files/library-structures.md @@ -23,11 +23,11 @@ 1. 如何获取代码库? - 比如,是否只能够从 npm 或 CDN 获取。 + 比如,是否只能够从 npm 或 CDN 获取。 2. 如何导入代码库? - 它是否添加了某个全局对象?它是否使用了`require`或`import`/`export`语句? + 它是否添加了某个全局对象?它是否使用了`require`或`import`/`export`语句? ## 针对不同类型的代码库的示例 @@ -70,22 +70,22 @@ define(..., ['someLib'], function(someLib) { 模块化代码库至少会包含以下代表性条目之一: -- 无条件的调用`require`或`define` -- 像`import * as a from 'b';`或`export c;`这样的声明 -- 赋值给`exports`或`module.exports` +- 无条件的调用`require`或`define` +- 像`import * as a from 'b';`或`export c;`这样的声明 +- 赋值给`exports`或`module.exports` 它们极少包含: -- 对`window`或`global`的赋值 +- 对`window`或`global`的赋值 #### 模块化代码库的模版 有以下四个模版可用: -- [`module.d.ts`](./templates/module.d.ts.md) -- [`module-class.d.ts`](./templates/module-class.d.ts.md) -- [`module-function.d.ts`](./templates/module-function.d.ts.md) -- [`module-plugin.d.ts`](./templates/module-plugin.d.ts.md) +- [`module.d.ts`](./templates/module.d.ts.md) +- [`module-class.d.ts`](./templates/module-class.d.ts.md) +- [`module-function.d.ts`](./templates/module-function.d.ts.md) +- [`module-plugin.d.ts`](./templates/module-plugin.d.ts.md) 你应该先阅读[`module.d.ts`](./templates/module.d.ts.md)以便从整体上了解它们的工作方式。 @@ -120,7 +120,7 @@ require('jest-matchers-files'); ```ts $(() => { - console.log('hello!'); + console.log('hello!'); }); ``` @@ -141,7 +141,7 @@ UMD 代码库与全局代码库很难通过文档来识别。 ```js function createGreeting(s) { - return 'Hello, ' + s; + return 'Hello, ' + s; } ``` @@ -149,22 +149,22 @@ function createGreeting(s) { ```js window.createGreeting = function (s) { - return 'Hello, ' + s; + return 'Hello, ' + s; }; ``` 在阅读全局代码库的代码时,你会看到: -- 顶层的`var`语句或`function`声明 -- 一个或多个`window.someName`赋值语句 -- 假设 DOM 相关的原始值`document`或`window`存在 +- 顶层的`var`语句或`function`声明 +- 一个或多个`window.someName`赋值语句 +- 假设 DOM 相关的原始值`document`或`window`存在 你不会看到: -- 检查或使用了模块加载器,如`require`或`define` -- CommonJS/Node.js 风格的导入语句,如`var fs = require("fs");` -- `define(...)`调用 -- 描述`require`或导入代码库的文档 +- 检查或使用了模块加载器,如`require`或`define` +- CommonJS/Node.js 风格的导入语句,如`var fs = require("fs");` +- `define(...)`调用 +- 描述`require`或导入代码库的文档 #### 全局代码库的示例 @@ -337,7 +337,7 @@ import * as someLib from 'someLib'; ```ts declare namespace cats { - interface KittySettings {} + interface KittySettings {} } ``` diff --git a/zh/declaration-files/publishing.md b/zh/declaration-files/publishing.md index 5a5ade33..eb2a7690 100644 --- a/zh/declaration-files/publishing.md +++ b/zh/declaration-files/publishing.md @@ -18,11 +18,11 @@ TypeScript 工程和 JavaScript 工程都可以使用[`--declaration`](/tsconfig ```json { - "name": "awesome", - "author": "Vandelay Industries", - "version": "1.0.0", - "main": "./lib/main.js", - "types": "./lib/main.d.ts" + "name": "awesome", + "author": "Vandelay Industries", + "version": "1.0.0", + "main": "./lib/main.js", + "types": "./lib/main.d.ts" } ``` @@ -38,16 +38,16 @@ TypeScript 工程和 JavaScript 工程都可以使用[`--declaration`](/tsconfig ```json { - "name": "browserify-typescript-extension", - "author": "Vandelay Industries", - "version": "1.0.0", - "main": "./lib/main.js", - "types": "./lib/main.d.ts", - "dependencies": { - "browserify": "latest", - "@types/browserify": "latest", - "typescript": "next" - } + "name": "browserify-typescript-extension", + "author": "Vandelay Industries", + "version": "1.0.0", + "main": "./lib/main.js", + "types": "./lib/main.d.ts", + "dependencies": { + "browserify": "latest", + "@types/browserify": "latest", + "typescript": "next" + } } ``` @@ -80,9 +80,9 @@ TypeScript 工程和 JavaScript 工程都可以使用[`--declaration`](/tsconfig 如果你的类型声明依赖于另一个包: -- *不要*把依赖的包放进你的包里,保持它们在各自的文件里。 -- *不要*将声明拷贝到你的包里。 -- *应该*依赖在 npm 上的类型声明包,如果依赖包没包含它自己的声明文件的话。 +- *不要*把依赖的包放进你的包里,保持它们在各自的文件里。 +- *不要*将声明拷贝到你的包里。 +- *应该*依赖在 npm 上的类型声明包,如果依赖包没包含它自己的声明文件的话。 ## 使用`typesVersions`选择版本 @@ -92,12 +92,12 @@ TypeScript 工程和 JavaScript 工程都可以使用[`--declaration`](/tsconfig ```json { - "name": "package-name", - "version": "1.0", - "types": "./index.d.ts", - "typesVersions": { - ">=3.1": { "*": ["ts3.1/*"] } - } + "name": "package-name", + "version": "1.0", + "types": "./index.d.ts", + "typesVersions": { + ">=3.1": { "*": ["ts3.1/*"] } + } } ``` @@ -121,13 +121,13 @@ TypeScript 是根据 Node.js 的[语言化版本](https://github.com/npm/node-se ```json tsconfig { - "name": "package-name", - "version": "1.0", - "types": "./index.d.ts", - "typesVersions": { - ">=3.2": { "*": ["ts3.2/*"] }, - ">=3.1": { "*": ["ts3.1/*"] } - } + "name": "package-name", + "version": "1.0", + "types": "./index.d.ts", + "typesVersions": { + ">=3.2": { "*": ["ts3.2/*"] }, + ">=3.1": { "*": ["ts3.1/*"] } + } } ``` @@ -136,14 +136,14 @@ TypeScript 是根据 Node.js 的[语言化版本](https://github.com/npm/node-se ```jsonc tsconfig { - "name": "package-name", - "version": "1.0", - "types": "./index.d.ts", - "typesVersions": { - // NOTE: this doesn't work! - ">=3.1": { "*": ["ts3.1/*"] }, - ">=3.2": { "*": ["ts3.2/*"] } - } + "name": "package-name", + "version": "1.0", + "types": "./index.d.ts", + "typesVersions": { + // NOTE: this doesn't work! + ">=3.1": { "*": ["ts3.1/*"] }, + ">=3.2": { "*": ["ts3.2/*"] } + } } ``` diff --git a/zh/declaration-files/templates.md b/zh/declaration-files/templates.md index f25676f6..2bd1a591 100644 --- a/zh/declaration-files/templates.md +++ b/zh/declaration-files/templates.md @@ -1,10 +1,9 @@ # 模板 -* [global-modifying-module.d.ts](templates/global-modifying-module.d.ts.md) -* [global-plugin.d.ts](templates/global-plugin.d.ts.md) -* [global.d.ts](templates/global.d.ts.md) -* [module-class.d.ts](templates/module-class.d.ts.md) -* [module-function.d.ts](templates/module-function.d.ts.md) -* [module-plugin.d.ts](templates/module-plugin.d.ts.md) -* [module.d.ts](templates/module.d.ts.md) - +- [global-modifying-module.d.ts](templates/global-modifying-module.d.ts.md) +- [global-plugin.d.ts](templates/global-plugin.d.ts.md) +- [global.d.ts](templates/global.d.ts.md) +- [module-class.d.ts](templates/module-class.d.ts.md) +- [module-function.d.ts](templates/module-function.d.ts.md) +- [module-plugin.d.ts](templates/module-plugin.d.ts.md) +- [module.d.ts](templates/module.d.ts.md) diff --git a/zh/declaration-files/templates/global-plugin.d.ts.md b/zh/declaration-files/templates/global-plugin.d.ts.md index 5ca0ad47..96cd2cbd 100644 --- a/zh/declaration-files/templates/global-plugin.d.ts.md +++ b/zh/declaration-files/templates/global-plugin.d.ts.md @@ -47,9 +47,9 @@ console.log(moment.format()); 针对模块,共存在三个模版。它们是: -- [`module.d.ts`](./templates/module.d.ts.md) -- [`module-class.d.ts`](./templates/module-class.d.ts.md) -- [`module-function.d.ts`](./templates/module-function.d.ts.md) +- [`module.d.ts`](./templates/module.d.ts.md) +- [`module-class.d.ts`](./templates/module-class.d.ts.md) +- [`module-function.d.ts`](./templates/module-function.d.ts.md) 若一个模块可以当作函数调用,则使用[`module-function.d.ts`](./templates/module-function.d.ts.md)。 @@ -203,7 +203,7 @@ import * as someLib from 'someLib'; ```ts declare namespace cats { - interface KittySettings {} + interface KittySettings {} } ``` @@ -243,7 +243,7 @@ var app = exp(); 一个代码库可以包含多个模块,比如: -``` +```txt myLib +---- index.js +---- foo.js @@ -263,7 +263,7 @@ var d = require('myLib/bar/baz'); 声明文件如下: -``` +```txt @types/myLib +---- index.d.ts +---- foo.d.ts @@ -284,22 +284,22 @@ var d = require('myLib/bar/baz'); *~ the built-in number type. */ interface Number { - toBinaryString(opts?: MyLibrary.BinaryFormatOptions): string; + toBinaryString(opts?: MyLibrary.BinaryFormatOptions): string; - toBinaryString( - callback: MyLibrary.BinaryFormatCallback, - opts?: MyLibrary.BinaryFormatOptions - ): string; + toBinaryString( + callback: MyLibrary.BinaryFormatCallback, + opts?: MyLibrary.BinaryFormatOptions + ): string; } /*~ If you need to declare several types, place them inside a namespace *~ to avoid adding too many things to the global namespace. */ declare namespace MyLibrary { - type BinaryFormatCallback = (n: number) => string; - interface BinaryFormatOptions { - prefix?: string; - padding: number; - } + type BinaryFormatCallback = (n: number) => string; + interface BinaryFormatOptions { + prefix?: string; + padding: number; + } } ``` diff --git a/zh/declaration-files/templates/global.d.ts.md b/zh/declaration-files/templates/global.d.ts.md index f82dfe2d..268e5a91 100644 --- a/zh/declaration-files/templates/global.d.ts.md +++ b/zh/declaration-files/templates/global.d.ts.md @@ -6,7 +6,7 @@ ```ts $(() => { - console.log('hello!'); + console.log('hello!'); }); ``` @@ -27,7 +27,7 @@ UMD 代码库与全局代码库很难通过文档来识别。 ```js function createGreeting(s) { - return 'Hello, ' + s; + return 'Hello, ' + s; } ``` @@ -35,22 +35,22 @@ function createGreeting(s) { ```js window.createGreeting = function (s) { - return 'Hello, ' + s; + return 'Hello, ' + s; }; ``` 在阅读全局代码库的代码时,你会看到: -- 顶层的`var`语句或`function`声明 -- 一个或多个`window.someName`赋值语句 -- 假设 DOM 相关的原始值`document`或`window`存在 +- 顶层的`var`语句或`function`声明 +- 一个或多个`window.someName`赋值语句 +- 假设 DOM 相关的原始值`document`或`window`存在 你不会看到: -- 检查或使用了模块加载器,如`require`或`define` -- CommonJS/Node.js 风格的导入语句,如`var fs = require("fs");` -- `define(...)`调用 -- 描述`require`或导入代码库的文档 +- 检查或使用了模块加载器,如`require`或`define` +- CommonJS/Node.js 风格的导入语句,如`var fs = require("fs");` +- `define(...)`调用 +- 描述`require`或导入代码库的文档 ## 全局代码库的示例 @@ -81,9 +81,9 @@ declare function myLib(a: number): number; *~ delete this declaration and add types inside the namespace below. */ interface myLib { - name: string; - length: number; - extras?: string[]; + name: string; + length: number; + extras?: string[]; } /*~ If your library has properties exposed on a global variable, @@ -91,37 +91,37 @@ interface myLib { *~ You should also place types (interfaces and type alias) here. */ declare namespace myLib { - //~ We can write 'myLib.timeout = 50;' - let timeout: number; - - //~ We can access 'myLib.version', but not change it - const version: string; - - //~ There's some class we can create via 'let c = new myLib.Cat(42)' - //~ Or reference e.g. 'function f(c: myLib.Cat) { ... } - class Cat { - constructor(n: number); - - //~ We can read 'c.age' from a 'Cat' instance - readonly age: number; - - //~ We can invoke 'c.purr()' from a 'Cat' instance - purr(): void; - } - - //~ We can declare a variable as - //~ 'var s: myLib.CatSettings = { weight: 5, name: "Maru" };' - interface CatSettings { - weight: number; - name: string; - tailLength?: number; - } - - //~ We can write 'const v: myLib.VetID = 42;' - //~ or 'const v: myLib.VetID = "bob";' - type VetID = string | number; - - //~ We can invoke 'myLib.checkCat(c)' or 'myLib.checkCat(c, v);' - function checkCat(c: Cat, s?: VetID); + //~ We can write 'myLib.timeout = 50;' + let timeout: number; + + //~ We can access 'myLib.version', but not change it + const version: string; + + //~ There's some class we can create via 'let c = new myLib.Cat(42)' + //~ Or reference e.g. 'function f(c: myLib.Cat) { ... } + class Cat { + constructor(n: number); + + //~ We can read 'c.age' from a 'Cat' instance + readonly age: number; + + //~ We can invoke 'c.purr()' from a 'Cat' instance + purr(): void; + } + + //~ We can declare a variable as + //~ 'var s: myLib.CatSettings = { weight: 5, name: "Maru" };' + interface CatSettings { + weight: number; + name: string; + tailLength?: number; + } + + //~ We can write 'const v: myLib.VetID = 42;' + //~ or 'const v: myLib.VetID = "bob";' + type VetID = string | number; + + //~ We can invoke 'myLib.checkCat(c)' or 'myLib.checkCat(c, v);' + function checkCat(c: Cat, s?: VetID); } ``` diff --git a/zh/handbook-v2/basics.md b/zh/handbook-v2/basics.md index 5fa852e0..e877238b 100644 --- a/zh/handbook-v2/basics.md +++ b/zh/handbook-v2/basics.md @@ -26,7 +26,7 @@ message(); 假设 `message` 是这样定义的: ```js -const message = "Hello World!"; +const message = 'Hello World!'; ``` 你可能很容易猜到,如果执行 `message.toLowerCase()`,我们将会得到一个所有字母都是小写的字符串。 @@ -65,7 +65,7 @@ function fn(x) { ```ts twoslash // @errors: 2349 -const message = "hello!"; +const message = 'hello!'; message(); ``` @@ -80,8 +80,8 @@ message(); ```js const user = { - name: 'Daniel', - age: 26, + name: 'Daniel', + age: 26, }; user.location; // 返回 undefined ``` @@ -91,7 +91,7 @@ user.location; // 返回 undefined ```ts twoslash // @errors: 2339 const user = { - name: "Daniel", + name: 'Daniel', age: 26, }; @@ -104,12 +104,12 @@ user.location; ```ts twoslash // @noErrors -const announcement = "Hello World!"; - +const announcement = 'Hello World!'; + // 你需要花多久才能注意到拼写错误? announcement.toLocaleLowercase(); announcement.toLocalLowerCase(); - + // 实际上正确的拼写是这样的…… announcement.toLocaleLowerCase(); ``` @@ -129,11 +129,11 @@ function flipCoin() { ```ts twoslash // @errors: 2367 -const value = Math.random() < 0.5 ? "a" : "b"; -if (value !== "a") { +const value = Math.random() < 0.5 ? 'a' : 'b'; +if (value !== 'a') { // ... -} else if (value === "b") { -// 永远无法到达这个分支 +} else if (value === 'b') { + // 永远无法到达这个分支 } ``` @@ -145,17 +145,15 @@ TypeScript 可以在我们的代码出现错误时捕获 bug。这很好,但 这意味着 TypeScript 也能用于编辑代码。我们在编辑器中输入的时候,核心的类型检查器能够提供报错信息和代码补全。人们经常会谈到 TypeScript 在工具层面的作用,这就是一个典型的例子。 - - -```ts twoslash +```ts // @noErrors // @esModuleInterop -import express from "express"; +import express from 'express'; const app = express(); -app.get("/", function (req, res) { +app.get('/', function (req, res) { res.sen -// ^| + // ^| }); app.listen(3000); @@ -207,7 +205,7 @@ function greet(person, date) { console.log(`Hello ${person}, today is ${date}!`); } -greet("Brendan"); +greet('Brendan'); ``` 如果我们再次执行 `tsc hello.ts`,那么会注意到命令行抛出了错误! @@ -252,7 +250,7 @@ function greet(person: string, date: Date) { console.log(`Hello ${person}, today is ${date.toDateString()}!`); } -greet("Maddison", Date()); +greet('Maddison', Date()); ``` 什么?TypeScript 报错提示第二个参数有问题,但这是为什么呢?你可能会有点惊讶,因为在 JavaScript 中直接调用 `Date()` 返回的是 `string`。另一方面,通过 `new Date()` 去构造 `Date`,则可以如预期那样返回 `Date` 对象。 @@ -263,14 +261,14 @@ greet("Maddison", Date()); function greet(person: string, date: Date) { console.log(`Hello ${person}, today is ${date.toDateString()}!`); } - -greet("Maddison", new Date()); + +greet('Maddison', new Date()); ``` 记住,我们并不总是需要显式地进行类型注解。在很多情况下,即使省略了类型注解,TypeScript 也可以为我们*推断出*(或者“搞清楚”)类型。 ```ts twoslash -let msg = "hello there!"; +let msg = 'hello there!'; // ^? ``` @@ -289,7 +287,7 @@ function greet(person: string, date: Date) { console.log(`Hello ${person}, today is ${date.toDateString()}!`); } -greet("Maddison", new Date()); +greet('Maddison', new Date()); ``` 注意到有两个变化: @@ -312,7 +310,7 @@ greet("Maddison", new Date()); 被重写为: ```js -"Hello " + person + ", today is " + date.toDateString() + "!"; +'Hello ' + person + ', today is ' + date.toDateString() + '!'; ``` 为什么会这样子呢? @@ -325,7 +323,7 @@ greet("Maddison", new Date()); function greet(person, date) { console.log(`Hello ${person}, today is ${date.toDateString()}!`); } -greet("Maddison", new Date()); +greet('Maddison', new Date()); ``` > 虽然默认的目标代码采用的是 ES3 语法,但现在浏览器大多数都已经支持 ES2015 了。 > diff --git a/zh/handbook-v2/classes.md b/zh/handbook-v2/classes.md index 68879c23..24f88136 100644 --- a/zh/handbook-v2/classes.md +++ b/zh/handbook-v2/classes.md @@ -36,7 +36,7 @@ pt.y = 0; 与其他位置一样,类型注解是可选的,但如果未指定,则会隐式为 `any` 类型。 -字段还可以有_初始化器_;当类被实例化时,它们将自动运行: +字段还可以有*初始化器*;当类被实例化时,它们将自动运行: ```ts twoslash class Point { @@ -59,7 +59,7 @@ class Point { } // ---cut--- const pt = new Point(); -pt.x = "0"; +pt.x = '0'; ``` #### `--strictPropertyInitialization` @@ -78,14 +78,14 @@ class GoodGreeter { name: string; constructor() { - this.name = "hello"; + this.name = 'hello'; } } ``` -请注意,字段需要在_构造函数内部_进行初始化。TypeScript 在检测初始化时不会分析从构造函数中调用的方法,因为派生类可能会覆写这些方法,并未初始化成员。 +请注意,字段需要在*构造函数内部*进行初始化。TypeScript 在检测初始化时不会分析从构造函数中调用的方法,因为派生类可能会覆写这些方法,并未初始化成员。 -如果你打算通过构造函数以外的方式明确初始化字段(例如,也许外部库填充类的一部分内容),你可以使用_明确赋值断言操作符_ `!`: +如果你打算通过构造函数以外的方式明确初始化字段(例如,也许外部库填充类的一部分内容),你可以使用*明确赋值断言操作符* `!`: ```ts twoslash class OKGreeter { @@ -101,7 +101,7 @@ class OKGreeter { ```ts twoslash // @errors: 2540 2540 class Greeter { - readonly name: string = "world"; + readonly name: string = 'world'; constructor(otherName?: string) { if (otherName !== undefined) { @@ -110,11 +110,11 @@ class Greeter { } err() { - this.name = "不可以"; + this.name = '不可以'; } } const g = new Greeter(); -g.name = "同样不可以"; +g.name = '同样不可以'; ``` ### 构造函数 @@ -185,7 +185,7 @@ class Derived extends Base {

-类中的函数属性称为_方法_。方法可以使用与函数和构造函数相同的类型注解: +类中的函数属性称为*方法*。方法可以使用与函数和构造函数相同的类型注解: ```ts twoslash class Point { @@ -208,18 +208,18 @@ class Point { let x: number = 0; class C { - x: string = "hello"; + x: string = 'hello'; m() { // 这是尝试修改第 1 行的‘x’,而不是类属性 - x = "world"; + x = 'world'; } } ``` ### Getter / Setter -类也可以拥有_访问器_: +类也可以拥有*访问器*: ```ts twoslash class C { @@ -299,13 +299,13 @@ interface Pingable { class Sonar implements Pingable { ping() { - console.log("ping!"); + console.log('ping!'); } } class Ball implements Pingable { pong() { - console.log("pong!"); + console.log('pong!'); } } ``` @@ -314,7 +314,7 @@ class Ball implements Pingable { #### 注意事项 -重要的是要理解,`implements` 子句仅仅是一个检查,用于判断类是否可以被视为接口类型。它_完全_不会改变类或其方法的类型。一个常见的错误是认为 `implements` 子句会改变类的类型——实际上并不会! +重要的是要理解,`implements` 子句仅仅是一个检查,用于判断类是否可以被视为接口类型。它*完全*不会改变类或其方法的类型。一个常见的错误是认为 `implements` 子句会改变类的类型——实际上并不会! ```ts twoslash // @errors: 7006 @@ -325,7 +325,7 @@ interface Checkable { class NameChecker implements Checkable { check(s) { // 注意这里没有错误 - return s.toLowerCase() === "ok"; + return s.toLowerCase() === 'ok'; // ^? } } @@ -361,14 +361,14 @@ c.y = 10; ```ts twoslash class Animal { move() { - console.log("继续前进!"); + console.log('继续前进!'); } } class Dog extends Animal { woof(times: number) { for (let i = 0; i < times; i++) { - console.log("汪!"); + console.log('汪!'); } } } @@ -397,7 +397,7 @@ TypeScript 强制要求派生类始终是其基类的子类型。 ```ts twoslash class Base { greet() { - console.log("你好,世界!"); + console.log('你好,世界!'); } } @@ -413,7 +413,7 @@ class Derived extends Base { const d = new Derived(); d.greet(); -d.greet("reader"); +d.greet('reader'); ``` 派生类必须遵循其基类的约定。请记住,通过基类引用来引用派生类实例是非常常见的做法(并且始终是合法的): @@ -421,7 +421,7 @@ d.greet("reader"); ```ts twoslash class Base { greet() { - console.log("你好,世界!"); + console.log('你好,世界!'); } } class Derived extends Base {} @@ -439,7 +439,7 @@ b.greet(); // @errors: 2416 class Base { greet() { - console.log("你好,世界!"); + console.log('你好,世界!'); } } @@ -500,14 +500,14 @@ JavaScript 类的初始化顺序在某些情况下可能会出人意料。让我 ```ts twoslash class Base { - name = "基础"; + name = '基础'; constructor() { - console.log("我是" + this.name); + console.log('我是' + this.name); } } class Derived extends Base { - name = "派生"; + name = '派生'; } // 输出“基础”,而不是“派生” @@ -541,7 +541,7 @@ class MsgError extends Error { super(m); } sayHello() { - return "你好" + this.message; + return '你好' + this.message; } } ``` @@ -563,7 +563,7 @@ class MsgError extends Error { } sayHello() { - return "hello " + this.message; + return 'hello ' + this.message; } } ``` @@ -583,7 +583,7 @@ class MsgError extends Error { ```ts twoslash class Greeter { public greet() { - console.log("嗨!"); + console.log('嗨!'); } } const g = new Greeter(); @@ -600,17 +600,17 @@ g.greet(); // @errors: 2445 class Greeter { public greet() { - console.log("你好," + this.getName()); + console.log('你好,' + this.getName()); } protected getName() { - return "嗨"; + return '嗨'; } } class SpecialGreeter extends Greeter { public howdy() { // 可以在这里访问受保护的成员 - console.log("Howdy, " + this.getName()); + console.log('Howdy, ' + this.getName()); // ^^^^^^^^^^^^^^ } } @@ -741,7 +741,7 @@ const s = new MySafe(); console.log(s.secretKey); ``` -`private` 还允许在类型检查期间使用方括号表示法进行访问。这使得对 `private` 声明的字段的访问在单元测试等情况下更容易,缺点是这些字段是_软私有的_,不严格执行私有化。 +`private` 还允许在类型检查期间使用方括号表示法进行访问。这使得对 `private` 声明的字段的访问在单元测试等情况下更容易,缺点是这些字段是*软私有的*,不严格执行私有化。 ```ts twoslash // @errors: 2341 @@ -755,15 +755,15 @@ const s = new MySafe(); console.log(s.secretKey); // 可以 -console.log(s["secretKey"]); +console.log(s['secretKey']); ``` -与 TypeScript 的 `private` 不同,JavaScript 的[私有字段](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Classes/Private_class_fields)(`#`)在编译后仍然保持私有,并且不提供先前提到的方括号访问等逃逸口,使得它们成为_硬私有_字段。 +与 TypeScript 的 `private` 不同,JavaScript 的[私有字段](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Classes/Private_class_fields)(`#`)在编译后仍然保持私有,并且不提供先前提到的方括号访问等逃逸口,使得它们成为*硬私有*字段。 ```ts twoslash class Dog { #barkAmount = 0; - personality = "happy"; + personality = 'happy'; constructor() {} } @@ -774,7 +774,7 @@ class Dog { // @showEmit class Dog { #barkAmount = 0; - personality = "happy"; + personality = 'happy'; constructor() {} } @@ -785,9 +785,9 @@ class Dog { ```ts twoslash // @target: es2015 // @showEmit -class Dog{ +class Dog { #barkAmount = 0; - personality = "happy"; + personality = 'happy'; constructor() {} } @@ -831,7 +831,7 @@ console.log(MyClass.x); ```ts twoslash class Base { static getGreeting() { - return "你好世界"; + return '你好世界'; } } class Derived extends Base { @@ -846,7 +846,7 @@ class Derived extends Base { ```ts twoslash // @errors: 2699 class S { - static name = "S!"; + static name = 'S!'; } ``` @@ -854,7 +854,7 @@ class S { TypeScript(以及 JavaScript)没有类似于 C# 的 `static class` 构造。 -这些构造的存在*仅*是因为这些语言强制要求所有的数据和函数都在类内部;因为 TypeScript 中不存在这种限制,所以也就没有必要使用它们。在 JavaScript/TypeScript 中,通常将只有一个实例的类表示为普通的_对象_。 +这些构造的存在*仅*是因为这些语言强制要求所有的数据和函数都在类内部;因为 TypeScript 中不存在这种限制,所以也就没有必要使用它们。在 JavaScript/TypeScript 中,通常将只有一个实例的类表示为普通的*对象*。 例如,在 TypeScript 中我们不需要“static class”语法,因为普通的对象(甚至是顶级函数)同样可以完成工作: @@ -878,22 +878,21 @@ const MyHelperObject = { 静态块允许你编写一系列具有自己作用域的语句,这些语句可以访问包含类中的私有字段。这意味着我们可以编写具有所有语句编写功能、没有变量泄漏以及对类内部的完全访问权限的初始化代码。 ```ts twoslash -declare function loadLastInstances(): any[] +declare function loadLastInstances(): any[]; // ---cut--- class Foo { - static #count = 0; + static #count = 0; - get count() { - return Foo.#count; - } + get count() { + return Foo.#count; + } - static { - try { - const lastInstances = loadLastInstances(); - Foo.#count += lastInstances.length; - } - catch {} - } + static { + try { + const lastInstances = loadLastInstances(); + Foo.#count += lastInstances.length; + } catch {} + } } ``` @@ -909,7 +908,7 @@ class Box { } } -const b = new Box("你好!"); +const b = new Box('你好!'); // ^? ``` @@ -942,14 +941,14 @@ JavaScript 对 `this` 的处理方式确实有些不寻常: ```ts twoslash class MyClass { - name = "MyClass"; + name = 'MyClass'; getName() { return this.name; } } const c = new MyClass(); const obj = { - name: "obj", + name: 'obj', getName: c.getName, }; @@ -973,7 +972,7 @@ console.log(obj.getName()); ```ts twoslash class MyClass { - name = "MyClass"; + name = 'MyClass'; getName = () => { return this.name; }; @@ -1015,7 +1014,7 @@ TypeScript 检查调用带有 `this` 参数的函数时,确保使用了正确 ```ts twoslash // @errors: 2684 class MyClass { - name = "MyClass"; + name = 'MyClass'; getName(this: MyClass) { return this.name; } @@ -1039,12 +1038,11 @@ console.log(g()); 在类中,一个特殊的类型 `this` *动态地*指向当前类的类型。让我们看一下它的用法: - -```ts twoslash +```ts class Box { - contents: string = ""; + contents: string = ''; set(value: string) { -// ^? + // ^? this.contents = value; return this; } @@ -1053,9 +1051,9 @@ class Box { 在这里,TypeScript 推断出 `set` 的返回类型是 `this`,而不是 `Box`。现在让我们创建 `Box` 的一个子类: -```ts twoslash +```ts class Box { - contents: string = ""; + contents: string = ''; set(value: string) { this.contents = value; return this; @@ -1064,20 +1062,20 @@ class Box { // ---cut--- class ClearableBox extends Box { clear() { - this.contents = ""; + this.contents = ''; } } const a = new ClearableBox(); -const b = a.set("你好"); +const b = a.set('你好'); // ^? ``` 你还可以在参数类型注释中使用 `this`: -```ts twoslash +```ts class Box { - content: string = ""; + content: string = ''; sameAs(other: this) { return other.content === this.content; } @@ -1089,14 +1087,14 @@ class Box { ```ts twoslash // @errors: 2345 class Box { - content: string = ""; + content: string = ''; sameAs(other: this) { return other.content === this.content; } } class DerivedBox extends Box { - otherContent: string = "?"; + otherContent: string = '?'; } const base = new Box(); @@ -1108,8 +1106,7 @@ derived.sameAs(base); 在类和接口的方法中,你可以在返回位置使用 `this is Type`。当与类型缩小(例如 `if` 语句)混合使用时,目标对象的类型将缩小为指定的 `Type`。 - -```ts twoslash +```ts // @strictPropertyInitialization: false class FileSystemObject { isFile(): this is FileRep { @@ -1138,17 +1135,17 @@ interface Networked { host: string; } -const fso: FileSystemObject = new FileRep("foo/bar.txt", "foo"); +const fso: FileSystemObject = new FileRep('foo/bar.txt', 'foo'); if (fso.isFile()) { fso.content; -// ^? + // ^? } else if (fso.isDirectory()) { fso.children; -// ^? + // ^? } else if (fso.isNetworked()) { fso.host; -// ^? + // ^? } ``` @@ -1164,7 +1161,7 @@ class Box { } const box = new Box(); -box.value = "Gameboy"; +box.value = 'Gameboy'; box.value; // ^? @@ -1214,7 +1211,7 @@ const someClass = class { } }; -const m = new someClass("你好,世界"); +const m = new someClass('你好,世界'); // ^? ``` @@ -1226,14 +1223,14 @@ JavaScript 类使用 `new` 运算符进行实例化。对于某个类本身的 class Point { createdAt: number; x: number; - y: number + y: number; constructor(x: number, y: number) { - this.createdAt = Date.now() + this.createdAt = Date.now(); this.x = x; this.y = y; } } -type PointInstance = InstanceType +type PointInstance = InstanceType; function moveRight(point: PointInstance) { point.x += 5; @@ -1260,7 +1257,7 @@ abstract class Base { abstract getName(): string; printName() { - console.log("你好," + this.getName()); + console.log('你好,' + this.getName()); } } @@ -1277,7 +1274,7 @@ abstract class Base { // ---cut--- class Derived extends Base { getName() { - return "世界"; + return '世界'; } } @@ -1313,7 +1310,7 @@ abstract class Base { } class Derived extends Base { getName() { - return ""; + return ''; } } // ---cut--- @@ -1342,7 +1339,7 @@ abstract class Base { } class Derived extends Base { getName() { - return ""; + return ''; } } // ---cut--- diff --git a/zh/handbook-v2/everyday-types.md b/zh/handbook-v2/everyday-types.md index 0d21a5a8..f05598be 100644 --- a/zh/handbook-v2/everyday-types.md +++ b/zh/handbook-v2/everyday-types.md @@ -35,7 +35,7 @@ let obj: any = { x: 0 }; obj.foo(); obj(); obj.bar = 100; -obj = "hello"; +obj = 'hello'; const n: number = obj; ``` @@ -52,7 +52,7 @@ const n: number = obj; 使用 `const`、`var` 或 `let` 声明变量时,你可以选择性地添加类型注解来显式指定变量的类型: ```ts twoslash -let myName: string = "Alice"; +let myName: string = 'Alice'; // ^^^^^^^^ 类型注解 ``` @@ -63,7 +63,7 @@ let myName: string = "Alice"; ```ts twoslash // 不需要类型注解——“myName”推断为 “string” 类型 -let myName = "Alice"; +let myName = 'Alice'; ``` 在大多数情况下,你不需要学习推断规则。如果你刚开始使用,尝试少使用一些类型注解——实际上仅需要了解少量的类型注解,就能让 TypeScript 完全理解代码的含义。 @@ -80,7 +80,7 @@ let myName = "Alice"; // 参数类型注解 function greet(name: string) { // ^^^^^^^^ - console.log("你好," + name.toUpperCase() + "!!"); + console.log('你好,' + name.toUpperCase() + '!!'); } ``` @@ -118,7 +118,7 @@ function getFavoriteNumber(): number { ```ts twoslash // @errors: 2551 // 这里没有类型注解,但 TypeScript 可以发现错误 -const names = ["Alice", "Bob", "Eve"]; +const names = ['Alice', 'Bob', 'Eve']; // 函数的上下文类型推断 names.forEach(function (s) { @@ -126,7 +126,7 @@ names.forEach(function (s) { }); // 箭头函数也适用上下文类型推断 -names.forEach((s) => { +names.forEach(s => { console.log(s.toUppercase()); }); ``` @@ -145,8 +145,8 @@ names.forEach((s) => { // 参数的类型注解是对象类型 function printCoord(pt: { x: number; y: number }) { // ^^^^^^^^^^^^^^^^^^^^^^^^ - console.log("坐标的 x 值是 " + pt.x); - console.log("坐标的 y 值是 " + pt.y); + console.log('坐标的 x 值是 ' + pt.x); + console.log('坐标的 y 值是 ' + pt.y); } printCoord({ x: 3, y: 7 }); ``` @@ -164,8 +164,8 @@ function printName(obj: { first: string; last?: string }) { // ... } // 都是有效的 -printName({ first: "Bob" }); -printName({ first: "Alice", last: "Alisson" }); +printName({ first: 'Bob' }); +printName({ first: 'Alice', last: 'Alisson' }); ``` 在 JavaScript 中,如果访问一个不存在的属性,你会得到 `undefined` 而不是运行时错误。因此,如果你*读取*的是一个可选属性的话,那么在使用它之前,你需要检查其是否为 `undefined`。 @@ -191,19 +191,19 @@ TypeScript 的类型系统允许你使用各种运算符从现有类型构建新 ### 定义联合类型 -*联合*(Union)类型是组合类型的一种方式。联合类型是由两个或更多其他类型形成的类型,表示值可以是这些类型中的*任意一个*。我们将每个类型都称为联合的*成员*。 +_联合_(Union)类型是组合类型的一种方式。联合类型是由两个或更多其他类型形成的类型,表示值可以是这些类型中的*任意一个*。我们将每个类型都称为联合的*成员*。 以下是可以操作字符串或数字的函数: ```ts twoslash // @errors: 2345 function printId(id: number | string) { - console.log("你的 ID 是:" + id); + console.log('你的 ID 是:' + id); } // 正常运行 printId(101); // 正常运行 -printId("202"); +printId('202'); // 错误 printId({ myID: 22342 }); ``` @@ -227,7 +227,7 @@ function printId(id: number | string) { ```ts twoslash function printId(id: number | string) { - if (typeof id === "string") { + if (typeof id === 'string') { // 在这个分支中,id 的类型是 'string' console.log(id.toUpperCase()); } else { @@ -243,10 +243,10 @@ function printId(id: number | string) { function welcomePeople(x: string[] | string) { if (Array.isArray(x)) { // 在这里:'x' 的类型是 'string[]' - console.log("你好," + x.join(" 和 ")); + console.log('你好,' + x.join(' 和 ')); } else { // 在这里:'x' 的类型是 'string' - console.log("欢迎,孤独旅行者 " + x); + console.log('欢迎,孤独旅行者 ' + x); } } ``` @@ -282,8 +282,8 @@ type Point = { // 与前面的示例完全相同 function printCoord(pt: Point) { - console.log("x 的坐标值是 " + pt.x); - console.log("y 的坐标值是 " + pt.y); + console.log('x 的坐标值是 ' + pt.x); + console.log('y 的坐标值是 ' + pt.y); } printCoord({ x: 100, y: 100 }); @@ -311,7 +311,7 @@ function sanitizeInput(str: string): UserInputSanitizedString { let userInput = sanitizeInput(getInput()); // 仍然可以使用字符串重新赋值 -userInput = "新的输入"; +userInput = '新的输入'; ``` ## 接口 @@ -325,8 +325,8 @@ interface Point { } function printCoord(pt: Point) { - console.log("x 的坐标值是 " + pt.x); - console.log("y 的坐标值是 " + pt.y); + console.log('x 的坐标值是 ' + pt.x); + console.log('y 的坐标值是 ' + pt.y); } printCoord({ x: 100, y: 100 }); @@ -354,7 +354,7 @@ interface Animal { interface Bear extends Animal { honey: boolean }
-const bear = getBear() +const bear = getBear() bear.name bear.honey @@ -365,8 +365,8 @@ bear.honey type Animal = { name: string }
-type Bear = Animal & { - honey: Boolean +type Bear = Animal & { + honey: Boolean }
const bear = getBear(); bear.name; @@ -406,7 +406,7 @@ type Window = { 在后面的章节中你会学到更多关于这些概念的知识,所以如果你没有立即理解这些知识,请不要担心。 -- 在 TypeScript 4.2 之前,类型别名命名[*可能* 会出现在错误消息中](/play?#code/PTAEGEHsFsAcEsA2BTATqNrLusgzngIYDm+oA7koqIYuYQJ56gCueyoAUCKAC4AWHAHaFcoSADMaQ0PCG80EwgGNkALk6c5C1EtWgAsqOi1QAb06groEbjWg8vVHOKcAvpokshy3vEgyyMr8kEbQJogAFND2YREAlOaW1soBeJAoAHSIkMTRmbbI8e6aPMiZxJmgACqCGKhY6ABGyDnkFFQ0dIzMbBwCwqIccabcYLyQoKjIEmh8kwN8DLAc5PzwwbLMyAAeK77IACYaQSEjUWZWhfYAjABMAMwALA+gbsVjoADqgjKESytQPxCHghAByXigYgBfr8LAsYj8aQMUASbDQcRSExCeCwFiIQh+AKfAYyBiQFgOPyIaikSGLQo0Zj-aazaY+dSaXjLDgAGXgAC9CKhDqAALxJaw2Ib2RzOISuDycLw+ImBYKQflCkWRRD2LXCw6JCxS1JCdJZHJ5RAFIbFJU8ADKC3WzEcnVZaGYE1ABpFnFOmsFhsil2uoHuzwArO9SmAAEIsSFrZB-GgAjjA5gtVN8VCEc1o1C4Q4AGlR2AwO1EsBQoAAbvB-gJ4HhPgB5aDwem-Ph1TCV3AEEirTp4ELtRbTPD4vwKjOfAuioSQHuDXBcnmgACC+eCONFEs73YAPGGZVT5cRyyhiHh7AAON7lsG3vBggB8XGV3l8-nVISOgghxoLq9i7io-AHsayRWGaFrlFauq2rg9qaIGQHwCBqChtKdgRo8TxRjeyB3o+7xAA),有时代替等效的匿名类型(可能需要也可能不需要)。接口在错误消息中将始终被命名。 +- 在 TypeScript 4.2 之前,类型别名命名[_可能_ 会出现在错误消息中](/play?#code/PTAEGEHsFsAcEsA2BTATqNrLusgzngIYDm+oA7koqIYuYQJ56gCueyoAUCKAC4AWHAHaFcoSADMaQ0PCG80EwgGNkALk6c5C1EtWgAsqOi1QAb06groEbjWg8vVHOKcAvpokshy3vEgyyMr8kEbQJogAFND2YREAlOaW1soBeJAoAHSIkMTRmbbI8e6aPMiZxJmgACqCGKhY6ABGyDnkFFQ0dIzMbBwCwqIccabcYLyQoKjIEmh8kwN8DLAc5PzwwbLMyAAeK77IACYaQSEjUWZWhfYAjABMAMwALA+gbsVjoADqgjKESytQPxCHghAByXigYgBfr8LAsYj8aQMUASbDQcRSExCeCwFiIQh+AKfAYyBiQFgOPyIaikSGLQo0Zj-aazaY+dSaXjLDgAGXgAC9CKhDqAALxJaw2Ib2RzOISuDycLw+ImBYKQflCkWRRD2LXCw6JCxS1JCdJZHJ5RAFIbFJU8ADKC3WzEcnVZaGYE1ABpFnFOmsFhsil2uoHuzwArO9SmAAEIsSFrZB-GgAjjA5gtVN8VCEc1o1C4Q4AGlR2AwO1EsBQoAAbvB-gJ4HhPgB5aDwem-Ph1TCV3AEEirTp4ELtRbTPD4vwKjOfAuioSQHuDXBcnmgACC+eCONFEs73YAPGGZVT5cRyyhiHh7AAON7lsG3vBggB8XGV3l8-nVISOgghxoLq9i7io-AHsayRWGaFrlFauq2rg9qaIGQHwCBqChtKdgRo8TxRjeyB3o+7xAA),有时代替等效的匿名类型(可能需要也可能不需要)。接口在错误消息中将始终被命名。 - 类型别名不能参与[声明合并,但接口可以](/play?#code/PTAEEEDtQS0gXApgJwGYEMDGjSfdAIx2UQFoB7AB0UkQBMAoEUfO0Wgd1ADd0AbAK6IAzizp16ALgYM4SNFhwBZdAFtV-UAG8GoPaADmNAcMmhh8ZHAMMAvjLkoM2UCvWad+0ARL0A-GYWVpA29gyY5JAWLJAwGnxmbvGgALzauvpGkCZmAEQAjABMAMwALLkANBl6zABi6DB8okR4Jjg+iPSgABboovDk3jjo5pbW1d6+dGb5djLwAJ7UoABKiJTwjThpnpnGpqPBoTLMAJrkArj4kOTwYmycPOhW6AR8IrDQ8N04wmo4HHQCwYi2Waw2W1S6S8HX8gTGITsQA)。 - 接口只能用于[声明对象的形状,不能重命名基本类型](/play?#code/PTAEAkFMCdIcgM6gC4HcD2pIA8CGBbABwBtIl0AzUAKBFAFcEBLAOwHMUBPQs0XFgCahWyGBVwBjMrTDJMAshOhMARpD4tQ6FQCtIE5DWoixk9QEEWAeV37kARlABvaqDegAbrmL1IALlAEZGV2agBfampkbgtrWwMAJlAAXmdXdy8ff0Dg1jZwyLoAVWZ2Lh5QVHUJflAlSFxROsY5fFAWAmk6CnRoLGwmILzQQmV8JmQmDzI-SOiKgGV+CaYAL0gBBdyy1KCQ-Pn1AFFplgA5enw1PtSWS+vCsAAVAAtB4QQWOEMKBuYVUiVCYvYQsUTQcRSBDGMGmKSgAAa-VEgiQe2GLgKQA)。 - 接口名称将[*始终*以其原始形式出现](/play?#code/PTAEGEHsFsAcEsA2BTATqNrLusgzngIYDm+oA7koqIYuYQJ56gCueyoAUCKAC4AWHAHaFcoSADMaQ0PCG80EwgGNkALk6c5C1EtWgAsqOi1QAb06groEbjWg8vVHOKcAvpokshy3vEgyyMr8kEbQJogAFND2YREAlOaW1soBeJAoAHSIkMTRmbbI8e6aPMiZxJmgACqCGKhY6ABGyDnkFFQ0dIzMbBwCwqIccabcYLyQoKjIEmh8kwN8DLAc5PzwwbLMyAAeK77IACYaQSEjUWY2Q-YAjABMAMwALA+gbsVjNXW8yxySoAADaAA0CCaZbPh1XYqXgOIY0ZgmcK0AA0nyaLFhhGY8F4AHJmEJILCWsgZId4NNfIgGFdcIcUTVfgBlZTOWC8T7kAJ42G4eT+GS42QyRaYbCgXAEEguTzeXyCjDBSAAQSE8Ai0Xsl0K9kcziExDeiQs1lAqSE6SyOTy0AKQ2KHk4p1V6s1OuuoHuzwArMagA)在错误消息中,但*只有*在按名称使用时才会出现。 @@ -422,7 +422,7 @@ type Window = { 在这种情况下,你可以使用*类型断言*来指定更具体的类型: ```ts twoslash -const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement; +const myCanvas = document.getElementById('main_canvas') as HTMLCanvasElement; ``` 与类型注解类似,类型断言会在编译时移除,不会影响代码的运行行为。 @@ -430,7 +430,7 @@ const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement; 你也可以使用尖括号语法(除非代码在 `.tsx` 文件中),效果是一样的: ```ts twoslash -const myCanvas = document.getElementById("main_canvas"); +const myCanvas = document.getElementById('main_canvas'); ``` > 提醒:由于类型断言在编译时被移除,因此没有与类型断言相关的运行时检查。 @@ -440,7 +440,7 @@ TypeScript 只允许将类型断言为*更具体*或*更不具体*的类型。 ```ts twoslash // @errors: 2352 -const x = "hello" as number; +const x = 'hello' as number; ``` 有时这个规则可能过于保守,会禁止一些更复杂的强制转换,尽管这些转换可能是有效的。如果遇到这种情况,你可以使用两个断言,先断言为 `any`(或者后面我们会介绍的 `unknown`),然后再断言为目标类型: @@ -459,13 +459,13 @@ const a = expr as any as T; 可以这样想,JavaScript 提供了不同的声明变量的方式。`var` 和 `let` 都允许改变变量中保存的值,而 `const` 则不允许。这体现在 TypeScript 创建字面类型的方式上。 ```ts twoslash -let changingString = "Hello World"; -changingString = "Olá Mundo"; +let changingString = 'Hello World'; +changingString = 'Olá Mundo'; // `changingString` 可以表示任意可能的字符串,所以 TypeScript 在类型系统中这样描述它 changingString; // ^? -const constantString = "Hello World"; +const constantString = 'Hello World'; // `constantString` 只能表示一个可能的字符串,它有字面类型的表示形式 constantString; // ^? @@ -475,11 +475,11 @@ constantString; ```ts twoslash // @errors: 2322 -let x: "hello" = "hello"; +let x: 'hello' = 'hello'; // OK -x = "hello"; +x = 'hello'; // ... -x = "howdy"; +x = 'howdy'; ``` 只能是固定一个值的变量并没有多大用处! @@ -488,11 +488,11 @@ x = "howdy"; ```ts twoslash // @errors: 2345 -function printText(s: string, alignment: "left" | "right" | "center") { +function printText(s: string, alignment: 'left' | 'right' | 'center') { // ... } -printText("Hello, world", "left"); -printText("G'day, mate", "centre"); +printText('Hello, world', 'left'); +printText("G'day, mate", 'centre'); ``` 数字字面类型的工作方式相同: @@ -510,12 +510,12 @@ function compare(a: string, b: string): -1 | 0 | 1 { interface Options { width: number; } -function configure(x: Options | "auto") { +function configure(x: Options | 'auto') { // ... } configure({ width: 100 }); -configure("auto"); -configure("automatic"); +configure('auto'); +configure('automatic'); ``` 还有一种字面类型:布尔字面类型。只有两种布尔字面类型,`true` 和 `false`。`boolean` 类型本身实际上只是 `true | false` 的联合类型的别名。 @@ -539,9 +539,9 @@ TypeScript 不会认为将 `1` 赋值给之前为 `0` 的字段是一个错误 ```ts twoslash // @errors: 2345 -declare function handleRequest(url: string, method: "GET" | "POST"): void; +declare function handleRequest(url: string, method: 'GET' | 'POST'): void; // ---cut--- -const req = { url: "https://example.com", method: "GET" }; +const req = { url: 'https://example.com', method: 'GET' }; handleRequest(req.url, req.method); ``` @@ -552,12 +552,12 @@ handleRequest(req.url, req.method); 1. 可以通过在任一位置添加类型断言来改变推断结果: ```ts twoslash - declare function handleRequest(url: string, method: "GET" | "POST"): void; + declare function handleRequest(url: string, method: 'GET' | 'POST'): void; // ---cut--- // 改变 1: - const req = { url: "https://example.com", method: "GET" as "GET" }; + const req = { url: 'https://example.com', method: 'GET' as 'GET' }; // 改变 2: - handleRequest(req.url, req.method as "GET"); + handleRequest(req.url, req.method as 'GET'); ``` 改变 1 的意思是 "我打算让 `req.method` 始终具有字面量类型 `"GET"`",阻止在之后将 `"GUESS"` 赋值给该字段。 @@ -566,9 +566,9 @@ handleRequest(req.url, req.method); 2. 可以使用 `as const` 将整个对象转换为字面量类型: ```ts twoslash - declare function handleRequest(url: string, method: "GET" | "POST"): void; + declare function handleRequest(url: string, method: 'GET' | 'POST'): void; // ---cut--- - const req = { url: "https://example.com", method: "GET" } as const; + const req = { url: 'https://example.com', method: 'GET' } as const; handleRequest(req.url, req.method); ``` @@ -582,18 +582,18 @@ TypeScript 也有两个相应的*类型*,名称相同。这些类型的特性 ### `strictNullChecks` 关闭 -如果 `strictNullChecks` *关闭*,可能为 `null` 或 `undefined` 的值仍然可以正常访问,并且可以将 `null` 和 `undefined` 赋值给任何类型的属性。这类似于没有空值检查的语言(例如 C#、Java)的行为。不检查这些值的缺失往往是错误的主要来源;建议尽可能打开 `strictNullChecks`。 +如果 `strictNullChecks` _关闭_,可能为 `null` 或 `undefined` 的值仍然可以正常访问,并且可以将 `null` 和 `undefined` 赋值给任何类型的属性。这类似于没有空值检查的语言(例如 C#、Java)的行为。不检查这些值的缺失往往是错误的主要来源;建议尽可能打开 `strictNullChecks`。 ### `strictNullChecks` 打开 -如果 `strictNullChecks` *打开*,当一个值为 `null` 或 `undefined` 时,你需要在使用该值的方法或属性之前进行检查。就像在使用可选属性之前检查 `undefined` 一样,我们可以使用*缩小类型*来检查可能为 `null` 的值: +如果 `strictNullChecks` _打开_,当一个值为 `null` 或 `undefined` 时,你需要在使用该值的方法或属性之前进行检查。就像在使用可选属性之前检查 `undefined` 一样,我们可以使用*缩小类型*来检查可能为 `null` 的值: ```ts twoslash function doSomething(x: string | null) { if (x === null) { // 什么都不做 } else { - console.log("Hello, " + x.toUpperCase()); + console.log('Hello, ' + x.toUpperCase()); } } ``` @@ -641,8 +641,8 @@ JavaScript 中有一个用于通过 `Symbol()` 函数创建全局唯一引用的 ```ts twoslash // @errors: 2367 -const firstName = Symbol("name"); -const secondName = Symbol("name"); +const firstName = Symbol('name'); +const secondName = Symbol('name'); if (firstName === secondName) { // 永远不会发生 diff --git a/zh/handbook-v2/modules.md b/zh/handbook-v2/modules.md index b0921b24..f504094f 100644 --- a/zh/handbook-v2/modules.md +++ b/zh/handbook-v2/modules.md @@ -50,7 +50,7 @@ export {}; ```ts twoslash // @filename: hello.ts export default function helloWorld() { - console.log("Hello, world!"); + console.log('Hello, world!'); } ``` @@ -59,11 +59,11 @@ export default function helloWorld() { ```ts twoslash // @filename: hello.ts export default function helloWorld() { - console.log("Hello, world!"); + console.log('Hello, world!'); } // @filename: index.ts // ---cut--- -import helloWorld from "./hello.js"; +import helloWorld from './hello.js'; helloWorld(); ``` @@ -97,7 +97,7 @@ export function absolute(num: number) { } // @filename: app.ts // ---cut--- -import { pi, phi, absolute } from "./maths.js"; +import { pi, phi, absolute } from './maths.js'; console.log(pi); const absPhi = absolute(phi); @@ -113,7 +113,7 @@ const absPhi = absolute(phi); export var pi = 3.14; // @filename: app.ts // ---cut--- -import { pi as π } from "./maths.js"; +import { pi as π } from './maths.js'; console.log(π); // ^? @@ -127,7 +127,7 @@ export const pi = 3.14; export default class RandomNumberGenerator {} // @filename: app.ts -import RandomNumberGenerator, { pi as π } from "./maths.js"; +import RandomNumberGenerator, { pi as π } from './maths.js'; RandomNumberGenerator; // ^? @@ -150,7 +150,7 @@ export function absolute(num: number) { } // ---cut--- // @filename: app.ts -import * as math from "./maths.js"; +import * as math from './maths.js'; console.log(math.pi); const positivePhi = math.absolute(math.phi); @@ -164,9 +164,9 @@ const positivePhi = math.absolute(math.phi); export var pi = 3.14; // ---cut--- // @filename: app.ts -import "./maths.js"; +import './maths.js'; -console.log("3.14"); +console.log('3.14'); ``` 在这种情况下,`import` 没有任何作用。然而,`maths.ts` 中的所有代码都被执行,这可能触发影响其他对象的副作用。 @@ -185,7 +185,7 @@ export interface Dog { } // @filename: app.ts -import { Cat, Dog } from "./animal.js"; +import { Cat, Dog } from './animal.js'; type Animals = Cat | Dog; ``` @@ -199,15 +199,15 @@ TypeScript 通过两个用来声明类型导入的概念,扩展了 `import` // @filename: animal.ts export type Cat = { breed: string; yearOfBirth: number }; export type Dog = { breeds: string[]; yearOfBirth: number }; -export const createCatName = () => "fluffy"; +export const createCatName = () => 'fluffy'; // @filename: valid.ts -import type { Cat, Dog } from "./animal.js"; +import type { Cat, Dog } from './animal.js'; export type Animals = Cat | Dog; // @filename: app.ts // @errors: 1361 -import type { createCatName } from "./animal.js"; +import type { createCatName } from './animal.js'; const name = createCatName(); ``` @@ -219,10 +219,10 @@ TypeScript 4.5 还允许在个别导入中使用 `type` 前缀,以指示被导 // @filename: animal.ts export type Cat = { breed: string; yearOfBirth: number }; export type Dog = { breeds: string[]; yearOfBirth: number }; -export const createCatName = () => "fluffy"; +export const createCatName = () => 'fluffy'; // ---cut--- // @filename: app.ts -import { createCatName, type Cat, type Dog } from "./animal.js"; +import { createCatName, type Cat, type Dog } from './animal.js'; export type Animals = Cat | Dog; const name = createCatName(); @@ -238,8 +238,8 @@ TypeScript 具有 ES 模块语法,它与 CommonJS 和 AMD 的 `require` *直 /// // @module: commonjs // ---cut--- -import fs = require("fs"); -const code = fs.readFileSync("hello.ts", "utf8"); +import fs = require('fs'); +const code = fs.readFileSync('hello.ts', 'utf8'); ``` 你可以在[模块参考页面](/docs/handbook/modules.html#export--and-import--require)了解更多关于此语法的信息。 @@ -287,7 +287,7 @@ module.exports = { }; // @filename: index.ts // ---cut--- -const maths = require("./maths"); +const maths = require('./maths'); maths.pi; // ^? ``` @@ -311,7 +311,7 @@ module.exports = { }; // @filename: index.ts // ---cut--- -const { squareTwo } = require("./maths"); +const { squareTwo } = require('./maths'); squareTwo; // ^? ``` @@ -348,7 +348,7 @@ TypeScript 包括两种解析策略:经典解析和 Node 解析。经典解析 export const valueOfPi = 3.142; // @filename: index.ts // ---cut--- -import { valueOfPi } from "./constants.js"; +import { valueOfPi } from './constants.js'; export const twoPi = valueOfPi * 2; ``` @@ -359,7 +359,7 @@ export const twoPi = valueOfPi * 2; // @showEmit // @module: es2020 // @noErrors -import { valueOfPi } from "./constants.js"; +import { valueOfPi } from './constants.js'; export const twoPi = valueOfPi * 2; ``` @@ -370,7 +370,7 @@ export const twoPi = valueOfPi * 2; // @showEmit // @module: commonjs // @noErrors -import { valueOfPi } from "./constants.js"; +import { valueOfPi } from './constants.js'; export const twoPi = valueOfPi * 2; ``` @@ -381,7 +381,7 @@ export const twoPi = valueOfPi * 2; // @showEmit // @module: umd // @noErrors -import { valueOfPi } from "./constants.js"; +import { valueOfPi } from './constants.js'; export const twoPi = valueOfPi * 2; ``` diff --git a/zh/handbook-v2/more-on-functions.md b/zh/handbook-v2/more-on-functions.md index 41feec98..c6b11aee 100644 --- a/zh/handbook-v2/more-on-functions.md +++ b/zh/handbook-v2/more-on-functions.md @@ -8,7 +8,7 @@ ```ts twoslash function greeter(fn: (a: string) => void) { - fn("Hello, World"); + fn('Hello, World'); } function printToConsole(s: string) { @@ -41,13 +41,13 @@ type DescribableFunction = { (someArg: number): boolean; }; function doSomething(fn: DescribableFunction) { - console.log(fn.description + " 返回了 " + fn(6)); + console.log(fn.description + ' 返回了 ' + fn(6)); } function myFunc(someArg: number) { return someArg > 3; } -myFunc.description = "默认描述"; +myFunc.description = '默认描述'; doSomething(myFunc); ``` @@ -65,7 +65,7 @@ type SomeConstructor = { new (s: string): SomeObject; }; function fn(ctor: SomeConstructor) { - return new ctor("你好"); + return new ctor('你好'); } ``` @@ -104,7 +104,7 @@ function firstElement(arr: Type[]): Type | undefined { declare function firstElement(arr: Type[]): Type | undefined; // ---cut--- // s 的类型是 'string' -const s = firstElement(["a", "b", "c"]); +const s = firstElement(['a', 'b', 'c']); // n 的类型是 'number' const n = firstElement([1, 2, 3]); // u 的类型是 undefined @@ -125,7 +125,7 @@ function map(arr: Input[], func: (arg: Input) => Output): Output[ // 参数 'n' 的类型是 'string' // 'parsed' 的类型是 'number[]' -const parsed = map(["1", "2", "3"], (n) => parseInt(n)); +const parsed = map(['1', '2', '3'], n => parseInt(n)); ``` 请注意,在这个示例中,TypeScript 可以(根据给定的 `string` 数组)推断出 `Input` 类型参数的类型,同时根据函数表达式的返回值(`number`)推断出 `Output` 类型参数的类型。 @@ -149,12 +149,12 @@ function longest(a: Type, b: Type) { // longerArray 的类型为 'number[]' const longerArray = longest([1, 2], [1, 2, 3]); // longerString 的类型为 'alice' | 'bob' -const longerString = longest("alice", "bob"); +const longerString = longest('alice', 'bob'); // 错误!数字没有 'length' 属性 const notOK = longest(10, 100); ``` -这个例子中有几个有趣的地方。我们允许 TypeScript *推断* `longest` 的返回类型。返回类型推断也适用于泛型函数。 +这个例子中有几个有趣的地方。我们允许 TypeScript _推断_ `longest` 的返回类型。返回类型推断也适用于泛型函数。 由于我们将 `Type` 约束为 `{ length: number }`,我们可以访问 `a` 和 `b` 参数的 `.length` 属性。如果没有类型约束,我们将无法访问这些属性,因为这些值可能是没有 length 属性的其他类型。 @@ -210,7 +210,7 @@ function combine(arr1: Type[], arr2: Type[]): Type[] { // @errors: 2322 declare function combine(arr1: Type[], arr2: Type[]): Type[]; // ---cut--- -const arr = combine([1, 2, 3], ["hello"]); +const arr = combine([1, 2, 3], ['hello']); ``` 然而,如果你打算这样做,可以手动指定 `Type`: @@ -218,7 +218,7 @@ const arr = combine([1, 2, 3], ["hello"]); ```ts twoslash declare function combine(arr1: Type[], arr2: Type[]): Type[]; // ---cut--- -const arr = combine([1, 2, 3], ["hello"]); +const arr = combine([1, 2, 3], ['hello']); ``` ### 编写良好的泛型函数的指南 @@ -275,17 +275,17 @@ function filter2 boolean>( ```ts twoslash function greet(s: Str) { - console.log("你好," + s); + console.log('你好,' + s); } -greet("世界"); +greet('世界'); ``` 我们也可以写一个更简单的版本: ```ts twoslash function greet(s: string) { - console.log("你好," + s); + console.log('你好,' + s); } ``` @@ -356,11 +356,11 @@ declare function myForEach( callback: (arg: any, index?: number) => void ): void; // ---cut--- -myForEach([1, 2, 3], (a) => console.log(a)); +myForEach([1, 2, 3], a => console.log(a)); myForEach([1, 2, 3], (a, i) => console.log(a, i)); ``` -然而,*实际上*这样的话 *`callback` 只可能会被传递一个参数*。换句话说,函数定义表示其实现可能如下所示: +然而,*实际上*这样的话 _`callback` 只可能会被传递一个参数_。换句话说,函数定义表示其实现可能如下所示: ```ts twoslash // @errors: 2532 18048 @@ -374,8 +374,7 @@ function myForEach(arr: any[], callback: (arg: any, index?: number) => void) { 然后,TypeScript 将强制执行这个含义,并发出实际上不可能的错误: - -```ts twoslash +```ts // @errors: 2532 18048 declare function myForEach( arr: any[], @@ -452,7 +451,7 @@ function fn(x: string): string; // 返回类型不正确 function fn(x: number): boolean; function fn(x: string | number) { - return "oops"; + return 'oops'; } ``` @@ -477,9 +476,9 @@ function len(x: any) { declare function len(s: string): number; declare function len(arr: any[]): number; // ---cut--- -len(""); // OK +len(''); // OK len([0]); // OK -len(Math.random() > 0.5 ? "hello" : [0]); +len(Math.random() > 0.5 ? 'hello' : [0]); ``` 由于两个重载具有相同的参数数量和相同的返回类型,我们可以使用非重载版本的函数: @@ -567,7 +566,7 @@ function noop() { ### `object` -特殊类型 `object` 指代任何非原始类型(`string`、`number`、`bigint`、`boolean`、`symbol`、`null` 或 `undefined`)的值。这与 *空对象类型* `{ }` 不同,也不同于全局类型 `Object`。你可能永远不会使用 `Object`。 +特殊类型 `object` 指代任何非原始类型(`string`、`number`、`bigint`、`boolean`、`symbol`、`null` 或 `undefined`)的值。这与 _空对象类型_ `{ }` 不同,也不同于全局类型 `Object`。你可能永远不会使用 `Object`。 > `object` 不是 `Object`。请**总是**使用 `object`! @@ -618,9 +617,9 @@ function fail(msg: string): never { ```ts twoslash function fn(x: string | number) { - if (typeof x === "string") { + if (typeof x === 'string') { // 做一些操作 - } else if (typeof x === "number") { + } else if (typeof x === 'number') { // 做另一些操作 } else { x; // 的类型为 'never'! @@ -659,7 +658,7 @@ function doSomething(f: Function) { ```ts twoslash function multiply(n: number, ...m: number[]) { - return m.map((x) => n * x); + return m.map(x => n * x); } // 'a' 的值为 [10, 20, 30, 40] const a = multiply(10, 1, 2, 3, 4); @@ -786,7 +785,7 @@ const v3 = f3(); const src = [1, 2, 3]; const dst = [0]; -src.forEach((el) => dst.push(el)); +src.forEach(el => dst.push(el)); ``` 还有另一种特殊情况需要注意,当字面函数定义的返回类型为 `void` 时,该函数必须**不**返回任何内容。 diff --git a/zh/handbook-v2/narrowing.md b/zh/handbook-v2/narrowing.md index 41d6c718..a903daa4 100644 --- a/zh/handbook-v2/narrowing.md +++ b/zh/handbook-v2/narrowing.md @@ -4,7 +4,7 @@ ```ts twoslash function padLeft(padding: number | string, input: string): string { - throw new Error("尚未实现!"); + throw new Error('尚未实现!'); } ``` @@ -13,7 +13,7 @@ function padLeft(padding: number | string, input: string): string { ```ts twoslash // @errors: 2345 function padLeft(padding: number | string, input: string) { - return " ".repeat(padding) + input; + return ' '.repeat(padding) + input; } ``` @@ -21,8 +21,8 @@ function padLeft(padding: number | string, input: string) { ```ts twoslash function padLeft(padding: number | string, input: string) { - if (typeof padding === "number") { - return " ".repeat(padding) + input; + if (typeof padding === 'number') { + return ' '.repeat(padding) + input; } return padding + input; } @@ -36,8 +36,8 @@ function padLeft(padding: number | string, input: string) { ```ts twoslash function padLeft(padding: number | string, input: string) { - if (typeof padding === "number") { - return " ".repeat(padding) + input; + if (typeof padding === 'number') { + return ' '.repeat(padding) + input; // ^? } return padding + input; @@ -67,11 +67,11 @@ TypeScript 可以理解几种不同的缩小类型的构造。 ```ts twoslash // @errors: 2531 18047 function printAll(strs: string | string[] | null) { - if (typeof strs === "object") { + if (typeof strs === 'object') { for (const s of strs) { console.log(s); } - } else if (typeof strs === "string") { + } else if (typeof strs === 'string') { console.log(strs); } else { // 什么都不做 @@ -96,7 +96,7 @@ function getUsersOnlineMessage(numUsersOnline: number) { if (numUsersOnline) { return `现在有 ${numUsersOnline} 人在线!`; } - return "这里没有人。 :("; + return '这里没有人。 :('; } ``` @@ -113,19 +113,19 @@ function getUsersOnlineMessage(numUsersOnline: number) { ```ts twoslash // 这两个都会得到 ‘true’ -Boolean("hello"); // 类型: boolean, 值: true -!!"world"; // 类型: true, 值: true +Boolean('hello'); // 类型: boolean, 值: true +!!'world'; // 类型: true, 值: true ``` 利用这种行为是相当流行的,特别是用于防范 `null` 或 `undefined` 等值。例如,让我们尝试将其应用于我们的 `printAll` 函数。 ```ts twoslash function printAll(strs: string | string[] | null) { - if (strs && typeof strs === "object") { + if (strs && typeof strs === 'object') { for (const s of strs) { console.log(s); } - } else if (typeof strs === "string") { + } else if (typeof strs === 'string') { console.log(strs); } } @@ -146,11 +146,11 @@ function printAll(strs: string | string[] | null) { // 继续阅读下去 // !!!!!!!!!!!!!!!! if (strs) { - if (typeof strs === "object") { + if (typeof strs === 'object') { for (const s of strs) { console.log(s); } - } else if (typeof strs === "string") { + } else if (typeof strs === 'string') { console.log(strs); } } @@ -171,7 +171,7 @@ function multiplyAll( if (!values) { return values; } else { - return values.map((x) => x * factor); + return values.map(x => x * factor); } } ``` @@ -204,12 +204,12 @@ function example(x: string | number, y: string | boolean) { ```ts twoslash function printAll(strs: string | string[] | null) { if (strs !== null) { - if (typeof strs === "object") { + if (typeof strs === 'object') { for (const s of strs) { // ^? console.log(s); } - } else if (typeof strs === "string") { + } else if (typeof strs === 'string') { console.log(strs); // ^? } @@ -247,7 +247,7 @@ type Fish = { swim: () => void }; type Bird = { fly: () => void }; function move(animal: Fish | Bird) { - if ("swim" in animal) { + if ('swim' in animal) { return animal.swim(); } @@ -257,8 +257,7 @@ function move(animal: Fish | Bird) { 需要强调的是,可选属性在缩小类型时将出现在两个分支中。例如,人类既可以游泳又可以飞行(通过正确的装备),因此应该在 `in` 检查的两个分支中都出现: - -```ts twoslash +```ts type Fish = { swim: () => void }; type Bird = { fly: () => void }; type Human = { swim?: () => void; fly?: () => void }; @@ -295,13 +294,13 @@ function logValue(x: Date | string) { 正如我们之前提到的,当我们对任何变量进行赋值时,TypeScript 会查看赋值语句的右侧,并相应地缩小左侧的类型。 ```ts twoslash -let x = Math.random() < 0.5 ? 10 : "hello world!"; +let x = Math.random() < 0.5 ? 10 : 'hello world!'; // ^? x = 1; console.log(x); // ^? -x = "goodbye!"; +x = 'goodbye!'; console.log(x); // ^? @@ -313,7 +312,7 @@ console.log(x); ```ts twoslash // @errors: 2322 -let x = Math.random() < 0.5 ? 10 : "hello world!"; +let x = Math.random() < 0.5 ? 10 : 'hello world!'; // ^? x = 1; @@ -331,8 +330,8 @@ console.log(x); ```ts twoslash function padLeft(padding: number | string, input: string) { - if (typeof padding === "number") { - return " ".repeat(padding) + input; + if (typeof padding === 'number') { + return ' '.repeat(padding) + input; } return padding + input; } @@ -352,7 +351,7 @@ function example() { // ^? if (Math.random() < 0.5) { - x = "hello"; + x = 'hello'; console.log(x); // ^? } else { @@ -423,7 +422,7 @@ const underWater2: Fish[] = zoo.filter(isFish) as Fish[]; // 对于更复杂的示例,可能需要重复使用类型断言 const underWater3: Fish[] = zoo.filter((pet): pet is Fish => { - if (pet.name === "sharkey") return false; + if (pet.name === 'sharkey') return false; return isFish(pet); }); ``` @@ -442,7 +441,7 @@ const underWater3: Fish[] = zoo.filter((pet): pet is Fish => { ```ts twoslash interface Shape { - kind: "circle" | "square"; + kind: 'circle' | 'square'; radius?: number; sideLength?: number; } @@ -453,7 +452,7 @@ interface Shape { ```ts twoslash // @errors: 2367 interface Shape { - kind: "circle" | "square"; + kind: 'circle' | 'square'; radius?: number; sideLength?: number; } @@ -461,7 +460,7 @@ interface Shape { // ---cut--- function handleShape(shape: Shape) { // 出错了! - if (shape.kind === "rect") { + if (shape.kind === 'rect') { // ... } } @@ -472,7 +471,7 @@ function handleShape(shape: Shape) { ```ts twoslash // @errors: 2532 18048 interface Shape { - kind: "circle" | "square"; + kind: 'circle' | 'square'; radius?: number; sideLength?: number; } @@ -488,14 +487,14 @@ function getArea(shape: Shape) { ```ts twoslash // @errors: 2532 18048 interface Shape { - kind: "circle" | "square"; + kind: 'circle' | 'square'; radius?: number; sideLength?: number; } // ---cut--- function getArea(shape: Shape) { - if (shape.kind === "circle") { + if (shape.kind === 'circle') { return Math.PI * shape.radius ** 2; } } @@ -505,14 +504,14 @@ function getArea(shape: Shape) { ```ts twoslash interface Shape { - kind: "circle" | "square"; + kind: 'circle' | 'square'; radius?: number; sideLength?: number; } // ---cut--- function getArea(shape: Shape) { - if (shape.kind === "circle") { + if (shape.kind === 'circle') { return Math.PI * shape.radius! ** 2; } } @@ -524,12 +523,12 @@ function getArea(shape: Shape) { ```ts twoslash interface Circle { - kind: "circle"; + kind: 'circle'; radius: number; } interface Square { - kind: "square"; + kind: 'square'; sideLength: number; } @@ -543,12 +542,12 @@ type Shape = Circle | Square; ```ts twoslash // @errors: 2339 interface Circle { - kind: "circle"; + kind: 'circle'; radius: number; } interface Square { - kind: "square"; + kind: 'square'; sideLength: number; } @@ -566,12 +565,12 @@ function getArea(shape: Shape) { ```ts twoslash interface Circle { - kind: "circle"; + kind: 'circle'; radius: number; } interface Square { - kind: "square"; + kind: 'square'; sideLength: number; } @@ -579,7 +578,7 @@ type Shape = Circle | Square; // ---cut--- function getArea(shape: Shape) { - if (shape.kind === "circle") { + if (shape.kind === 'circle') { return Math.PI * shape.radius ** 2; // ^? } @@ -594,12 +593,12 @@ function getArea(shape: Shape) { ```ts twoslash interface Circle { - kind: "circle"; + kind: 'circle'; radius: number; } interface Square { - kind: "square"; + kind: 'square'; sideLength: number; } @@ -608,10 +607,10 @@ type Shape = Circle | Square; // ---cut--- function getArea(shape: Shape) { switch (shape.kind) { - case "circle": + case 'circle': return Math.PI * shape.radius ** 2; // ^? - case "square": + case 'square': return shape.sideLength ** 2; // ^? } @@ -637,12 +636,12 @@ function getArea(shape: Shape) { ```ts twoslash interface Circle { - kind: "circle"; + kind: 'circle'; radius: number; } interface Square { - kind: "square"; + kind: 'square'; sideLength: number; } // ---cut--- @@ -650,9 +649,9 @@ type Shape = Circle | Square; function getArea(shape: Shape) { switch (shape.kind) { - case "circle": + case 'circle': return Math.PI * shape.radius ** 2; - case "square": + case 'square': return shape.sideLength ** 2; default: const _exhaustiveCheck: never = shape; @@ -666,17 +665,17 @@ function getArea(shape: Shape) { ```ts twoslash // @errors: 2322 interface Circle { - kind: "circle"; + kind: 'circle'; radius: number; } interface Square { - kind: "square"; + kind: 'square'; sideLength: number; } // ---cut--- interface Triangle { - kind: "triangle"; + kind: 'triangle'; sideLength: number; } @@ -684,9 +683,9 @@ type Shape = Circle | Square | Triangle; function getArea(shape: Shape) { switch (shape.kind) { - case "circle": + case 'circle': return Math.PI * shape.radius ** 2; - case "square": + case 'square': return shape.sideLength ** 2; default: const _exhaustiveCheck: never = shape; diff --git a/zh/handbook-v2/object-types.md b/zh/handbook-v2/object-types.md index bb96a4fb..be27de13 100644 --- a/zh/handbook-v2/object-types.md +++ b/zh/handbook-v2/object-types.md @@ -455,8 +455,7 @@ interface BasicAddress { 在某些情况下,这已经足够了,但是地址经常会有一个单元号与之关联,比如某个地址对应的建筑物有多个单元。我们可以描述 `AddressWithUnit` 类型。 - -```ts twoslash +```ts interface AddressWithUnit { name?: string; unit: string; diff --git a/zh/handbook/basic-types.md b/zh/handbook/basic-types.md index 437e126d..1e35caa2 100644 --- a/zh/handbook/basic-types.md +++ b/zh/handbook/basic-types.md @@ -2,11 +2,11 @@ ## 介绍 -为了让程序有价值,我们需要能够处理最简单的数据单元:数字,字符串,结构体,布尔值等。 TypeScript支持与JavaScript几乎相同的数据类型,此外还提供了实用的枚举类型方便我们使用。 +为了让程序有价值,我们需要能够处理最简单的数据单元:数字,字符串,结构体,布尔值等。 TypeScript 支持与 JavaScript 几乎相同的数据类型,此外还提供了实用的枚举类型方便我们使用。 ## Boolean -最基本的数据类型就是简单的true/false值,在JavaScript和TypeScript里叫做`boolean`(其它语言中也一样)。 +最基本的数据类型就是简单的 true/false 值,在 JavaScript 和 TypeScript 里叫做`boolean`(其它语言中也一样)。 ```typescript let isDone: boolean = false; @@ -14,7 +14,7 @@ let isDone: boolean = false; ## Number -和JavaScript一样,TypeScript里的所有数字都是浮点数或者大整数 。 这些浮点数的类型是`number`, 而大整数的类型则是 `bigint`。 除了支持十进制和十六进制字面量,TypeScript还支持ECMAScript 2015中引入的二进制和八进制字面量。 +和 JavaScript 一样,TypeScript 里的所有数字都是浮点数或者大整数 。 这些浮点数的类型是`number`, 而大整数的类型则是 `bigint`。 除了支持十进制和十六进制字面量,TypeScript 还支持 ECMAScript 2015 中引入的二进制和八进制字面量。 ```typescript let decLiteral: number = 6; @@ -26,33 +26,38 @@ let bigLiteral: bigint = 100n; ## String -JavaScript程序的另一项基本操作是处理网页或服务器端的文本数据。 像其它语言里一样,我们使用`string`表示文本数据类型。 和JavaScript一样,可以使用双引号(`"`)或单引号(`'`)表示字符串。 +JavaScript 程序的另一项基本操作是处理网页或服务器端的文本数据。 像其它语言里一样,我们使用`string`表示文本数据类型。 和 JavaScript 一样,可以使用双引号(`"`)或单引号(`'`)表示字符串。 ```typescript -let name: string = "bob"; -name = "smith"; +let name: string = 'bob'; +name = 'smith'; ``` -你还可以使用_模版字符串_,它可以定义多行文本和内嵌表达式。 这种字符串是被反引号包围(\` \`),并且以`${ expr }`这种形式嵌入表达式 +你还可以使用*模版字符串*,它可以定义多行文本和内嵌表达式。 这种字符串是被反引号包围(\` \`),并且以`${ expr }`这种形式嵌入表达式 ```typescript let name: string = `Gene`; let age: number = 37; -let sentence: string = `Hello, my name is ${ name }. +let sentence: string = `Hello, my name is ${name}. -I'll be ${ age + 1 } years old next month.`; +I'll be ${age + 1} years old next month.`; ``` 这与下面定义`sentence`的方式效果相同: ```typescript -let sentence: string = "Hello, my name is " + name + ".\n\n" + - "I'll be " + (age + 1) + " years old next month."; +let sentence: string = + 'Hello, my name is ' + + name + + '.\n\n' + + "I'll be " + + (age + 1) + + ' years old next month.'; ``` ## Array -TypeScript像JavaScript一样可以操作数组元素。 有两种方式可以定义数组。 第一种,可以在元素类型后面接上`[]`,表示由此类型元素组成的一个数组: +TypeScript 像 JavaScript 一样可以操作数组元素。 有两种方式可以定义数组。 第一种,可以在元素类型后面接上`[]`,表示由此类型元素组成的一个数组: ```typescript let list: number[] = [1, 2, 3]; @@ -87,41 +92,57 @@ console.log(x[1].substr(1)); // Error, 'number' does not have 'substr' 当访问一个越界的元素会报错。 ```typescript -x[3] = "world"; // Error, Property '3' does not exist on type '[string, number]'. +x[3] = 'world'; // Error, Property '3' does not exist on type '[string, number]'. console.log(x[5].toString()); // Error, Property '5' does not exist on type '[string, number]'. ``` ## Enum -`enum`类型是对JavaScript标准数据类型的一个补充。 像C\#等其它语言一样,使用枚举类型可以为一组数值赋予友好的名字。 +`enum`类型是对 JavaScript 标准数据类型的一个补充。 像 C\#等其它语言一样,使用枚举类型可以为一组数值赋予友好的名字。 ```typescript -enum Color {Red, Green, Blue} +enum Color { + Red, + Green, + Blue, +} let c: Color = Color.Green; ``` 默认情况下,从`0`开始为元素编号。 你也可以手动的指定成员的数值。 例如,我们将上面的例子改成从`1`开始编号: ```typescript -enum Color {Red = 1, Green, Blue} +enum Color { + Red = 1, + Green, + Blue, +} let c: Color = Color.Green; ``` 或者,全部都采用手动赋值: ```typescript -enum Color {Red = 1, Green = 2, Blue = 4} +enum Color { + Red = 1, + Green = 2, + Blue = 4, +} let c: Color = Color.Green; ``` -枚举类型提供的一个便利是你可以由枚举的值得到它的名字。 例如,我们知道数值为2,但是不确定它映射到Color里的哪个名字,我们可以查找相应的名字: +枚举类型提供的一个便利是你可以由枚举的值得到它的名字。 例如,我们知道数值为 2,但是不确定它映射到 Color 里的哪个名字,我们可以查找相应的名字: ```typescript -enum Color {Red = 1, Green, Blue} +enum Color { + Red = 1, + Green, + Blue, +} let colorName: string = Color[2]; -console.log(colorName); // 显示'Green'因为上面代码里它的值是2 +console.log(colorName); // 显示'Green'因为上面代码里它的值是2 ``` ## Unknown @@ -130,7 +151,7 @@ console.log(colorName); // 显示'Green'因为上面代码里它的值是2 ```typescript let notSure: unknown = 4; -notSure = "maybe a string instead"; +notSure = 'maybe a string instead'; // OK, definitely a boolean notSure = false; @@ -151,7 +172,7 @@ if (maybe === true) { const aString: string = maybe; } -if (typeof maybe === "string") { +if (typeof maybe === 'string') { // TypeScript knows that maybe is a string const aString: string = maybe; // So, it cannot be a boolean @@ -165,7 +186,7 @@ if (typeof maybe === "string") { ```typescript let notSure: any = 4; -notSure = "maybe a string instead"; +notSure = 'maybe a string instead'; notSure = false; // okay, definitely a boolean ``` @@ -185,7 +206,7 @@ prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object 当你只知道一部分数据的类型时,`any`类型也是有用的。 比如,你有一个数组,它包含了不同的类型的数据: ```typescript -let list: any[] = [1, true, "free"]; +let list: any[] = [1, true, 'free']; list[1] = 100; ``` @@ -196,7 +217,7 @@ list[1] = 100; ```typescript function warnUser(): void { - console.log("This is my warning message"); + console.log('This is my warning message'); } ``` @@ -208,7 +229,7 @@ let unusable: void = undefined; ## Null 和 Undefined -TypeScript里,`undefined`和`null`两者各自有自己的类型分别叫做`undefined`和`null`。 和`void`相似,它们的本身的类型用处不是很大: +TypeScript 里,`undefined`和`null`两者各自有自己的类型分别叫做`undefined`和`null`。 和`void`相似,它们的本身的类型用处不是很大: ```typescript // Not much else we can assign to these variables! @@ -218,7 +239,7 @@ let n: null = null; 默认情况下`null`和`undefined`是所有类型的子类型。 就是说你可以把`null`和`undefined`赋值给`number`类型的变量。 -然而,当你指定了`--strictNullChecks`标记,`null`和`undefined`只能赋值给`any`和它们各自的类型(有一个例外是`undefined`还可以赋值给`void`类型)。 这能避免_很多_常见的问题。 也许在某处你想传入一个`string`或`null`或`undefined`,你可以使用联合类型`string | null | undefined`。 +然而,当你指定了`--strictNullChecks`标记,`null`和`undefined`只能赋值给`any`和它们各自的类型(有一个例外是`undefined`还可以赋值给`void`类型)。 这能避免*很多*常见的问题。 也许在某处你想传入一个`string`或`null`或`undefined`,你可以使用联合类型`string | null | undefined`。 联合类型是高级主题,我们会在以后的章节里讨论它。 @@ -228,25 +249,24 @@ let n: null = null; `never`类型表示的是那些永不存在的值的类型。 例如,`never`类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型; 变量也可能是`never`类型,当它们被永不为真的类型保护所约束时。 -`never`类型是任何类型的子类型,也可以赋值给任何类型;然而,_没有_类型是`never`的子类型或可以赋值给`never`类型(除了`never`本身之外)。 即使`any`也不可以赋值给`never`。 +`never`类型是任何类型的子类型,也可以赋值给任何类型;然而,*没有*类型是`never`的子类型或可以赋值给`never`类型(除了`never`本身之外)。 即使`any`也不可以赋值给`never`。 下面是一些返回`never`类型的函数: ```typescript // 返回never的函数必须存在无法达到的终点 function error(message: string): never { - throw new Error(message); + throw new Error(message); } // 推断的返回值类型为never function fail() { - return error("Something failed"); + return error('Something failed'); } // 返回never的函数必须存在无法达到的终点 function infiniteLoop(): never { - while (true) { - } + while (true) {} } ``` @@ -254,7 +274,7 @@ function infiniteLoop(): never { `object`表示非原始类型,也就是除`number`,`string`,`boolean`,`bigint`,`symbol`,`null`或`undefined`之外的类型。 -使用`object`类型,就可以更好的表示像`Object.create`这样的API。例如: +使用`object`类型,就可以更好的表示像`Object.create`这样的 API。例如: ```typescript declare function create(o: object | null): void; @@ -263,21 +283,21 @@ create({ prop: 0 }); // OK create(null); // OK create(42); // Error -create("string"); // Error +create('string'); // Error create(false); // Error create(undefined); // Error ``` ## 类型断言 -有时候你会遇到这样的情况,你会比TypeScript更了解某个值的详细信息。 通常这会发生在你清楚地知道一个实体具有比它现有类型更确切的类型。 +有时候你会遇到这样的情况,你会比 TypeScript 更了解某个值的详细信息。 通常这会发生在你清楚地知道一个实体具有比它现有类型更确切的类型。 -通过_类型断言_这种方式可以告诉编译器,“相信我,我知道自己在干什么”。 类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。 它没有运行时的影响,只是在编译阶段起作用。 TypeScript会假设你,程序员,已经进行了必须的检查。 +通过*类型断言*这种方式可以告诉编译器,“相信我,我知道自己在干什么”。 类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。 它没有运行时的影响,只是在编译阶段起作用。 TypeScript 会假设你,程序员,已经进行了必须的检查。 类型断言有两种形式。 其一是“尖括号”语法: ```typescript -let someValue: any = "this is a string"; +let someValue: any = 'this is a string'; let strLength: number = (someValue).length; ``` @@ -285,16 +305,16 @@ let strLength: number = (someValue).length; 另一个为`as`语法: ```typescript -let someValue: any = "this is a string"; +let someValue: any = 'this is a string'; let strLength: number = (someValue as string).length; ``` -两种形式是等价的。 至于使用哪个大多数情况下是凭个人喜好;然而,当你在TypeScript里使用JSX时,只有`as`语法断言是被允许的。 +两种形式是等价的。 至于使用哪个大多数情况下是凭个人喜好;然而,当你在 TypeScript 里使用 JSX 时,只有`as`语法断言是被允许的。 ## 关于`let` -你可能已经注意到了,我们使用`let`关键字来代替大家所熟悉的JavaScript关键字`var`。 `let`是ES2015引入的关键字,它比`var`更加安全,因此被看做是声明变量的标准方式。 我们会在以后详细介绍它,很多常见的问题都可以通过使用`let`来解决,所以尽可能地使用`let`来代替`var`吧。 +你可能已经注意到了,我们使用`let`关键字来代替大家所熟悉的 JavaScript 关键字`var`。 `let`是 ES2015 引入的关键字,它比`var`更加安全,因此被看做是声明变量的标准方式。 我们会在以后详细介绍它,很多常见的问题都可以通过使用`let`来解决,所以尽可能地使用`let`来代替`var`吧。 ## 关于 Number, String, Boolean, Symbol 和 Object @@ -303,19 +323,18 @@ let strLength: number = (someValue as string).length; ```typescript // @errors: 2339 function reverse(s: String): String { - return s.split("").reverse().join(""); + return s.split('').reverse().join(''); } -reverse("hello world"); +reverse('hello world'); ``` 相对地,我们应该使用 `number`、`string`、`boolean`、`object` 和 `symbol` ```typescript function reverse(s: string): string { - return s.split("").reverse().join(""); + return s.split('').reverse().join(''); } -reverse("hello world"); +reverse('hello world'); ``` - diff --git a/zh/handbook/classes.md b/zh/handbook/classes.md index a753cc36..f9a18b2c 100644 --- a/zh/handbook/classes.md +++ b/zh/handbook/classes.md @@ -2,7 +2,7 @@ ## 介绍 -传统的JavaScript程序使用函数和基于原型的继承来创建可重用的组件,但对于熟悉使用面向对象方式的程序员来讲就有些棘手,因为他们用的是基于类的继承并且对象是由类构建出来的。 从ECMAScript 2015,也就是ECMAScript 6开始,JavaScript程序员将能够使用基于类的面向对象的方式。 使用TypeScript,我们允许开发者现在就使用这些特性,并且编译后的JavaScript可以在所有主流浏览器和平台上运行,而不需要等到下个JavaScript版本。 +传统的 JavaScript 程序使用函数和基于原型的继承来创建可重用的组件,但对于熟悉使用面向对象方式的程序员来讲就有些棘手,因为他们用的是基于类的继承并且对象是由类构建出来的。 从 ECMAScript 2015,也就是 ECMAScript 6 开始,JavaScript 程序员将能够使用基于类的面向对象的方式。 使用 TypeScript,我们允许开发者现在就使用这些特性,并且编译后的 JavaScript 可以在所有主流浏览器和平台上运行,而不需要等到下个 JavaScript 版本。 ## 类 @@ -10,19 +10,19 @@ ```typescript class Greeter { - greeting: string; - constructor(message: string) { - this.greeting = message; - } - greet() { - return "Hello, " + this.greeting; - } + greeting: string; + constructor(message: string) { + this.greeting = message; + } + greet() { + return 'Hello, ' + this.greeting; + } } -let greeter = new Greeter("world"); +let greeter = new Greeter('world'); ``` -如果你使用过C\#或Java,你会对这种语法非常熟悉。 我们声明一个`Greeter`类。这个类有3个成员:一个叫做`greeting`的属性,一个构造函数和一个`greet`方法。 +如果你使用过 C\#或 Java,你会对这种语法非常熟悉。 我们声明一个`Greeter`类。这个类有 3 个成员:一个叫做`greeting`的属性,一个构造函数和一个`greet`方法。 你会注意到,我们在引用任何一个类成员的时候都用了`this`。 它表示我们访问的是类的成员。 @@ -30,21 +30,21 @@ let greeter = new Greeter("world"); ## 继承 -在TypeScript里,我们可以使用常用的面向对象模式。 基于类的程序设计中一种最基本的模式是允许使用继承来扩展现有的类。 +在 TypeScript 里,我们可以使用常用的面向对象模式。 基于类的程序设计中一种最基本的模式是允许使用继承来扩展现有的类。 看下面的例子: ```typescript class Animal { - move(distanceInMeters: number = 0) { - console.log(`Animal moved ${distanceInMeters}m.`); - } + move(distanceInMeters: number = 0) { + console.log(`Animal moved ${distanceInMeters}m.`); + } } class Dog extends Animal { - bark() { - console.log('Woof! Woof!'); - } + bark() { + console.log('Woof! Woof!'); + } } const dog = new Dog(); @@ -53,7 +53,7 @@ dog.move(10); dog.bark(); ``` -这个例子展示了最基本的继承:类从基类中继承了属性和方法。 这里,`Dog`是一个_派生类_,它派生自`Animal`_基类_,通过`extends`关键字。 派生类通常被称作_子类_,基类通常被称作_超类_。 +这个例子展示了最基本的继承:类从基类中继承了属性和方法。 这里,`Dog`是一个*派生类*,它派生自`Animal`_基类_,通过`extends`关键字。 派生类通常被称作*子类*,基类通常被称作*超类*。 因为`Dog`继承了`Animal`的功能,因此我们可以创建一个`Dog`的实例,它能够`bark()`和`move()`。 @@ -61,31 +61,37 @@ dog.bark(); ```typescript class Animal { - name: string; - constructor(theName: string) { this.name = theName; } - move(distanceInMeters: number = 0) { - console.log(`${this.name} moved ${distanceInMeters}m.`); - } + name: string; + constructor(theName: string) { + this.name = theName; + } + move(distanceInMeters: number = 0) { + console.log(`${this.name} moved ${distanceInMeters}m.`); + } } class Snake extends Animal { - constructor(name: string) { super(name); } - move(distanceInMeters = 5) { - console.log("Slithering..."); - super.move(distanceInMeters); - } + constructor(name: string) { + super(name); + } + move(distanceInMeters = 5) { + console.log('Slithering...'); + super.move(distanceInMeters); + } } class Horse extends Animal { - constructor(name: string) { super(name); } - move(distanceInMeters = 45) { - console.log("Galloping..."); - super.move(distanceInMeters); - } + constructor(name: string) { + super(name); + } + move(distanceInMeters = 45) { + console.log('Galloping...'); + super.move(distanceInMeters); + } } -let sam = new Snake("Sammy the Python"); -let tom: Animal = new Horse("Tommy the Palomino"); +let sam = new Snake('Sammy the Python'); +let tom: Animal = new Horse('Tommy the Palomino'); sam.move(); tom.move(34); @@ -93,7 +99,7 @@ tom.move(34); 这个例子展示了一些上面没有提到的特性。 这一次,我们使用`extends`关键字创建了`Animal`的两个子类:`Horse`和`Snake`。 -与前一个例子的不同点是,派生类包含了一个构造函数,它_必须_调用`super()`,它会执行基类的构造函数。 而且,在构造函数里访问`this`的属性之前,我们_一定_要调用`super()`。 这个是TypeScript强制执行的一条重要规则。 +与前一个例子的不同点是,派生类包含了一个构造函数,它*必须*调用`super()`,它会执行基类的构造函数。 而且,在构造函数里访问`this`的属性之前,我们*一定*要调用`super()`。 这个是 TypeScript 强制执行的一条重要规则。 这个例子演示了如何在子类里可以重写父类的方法。 `Snake`类和`Horse`类都创建了`move`方法,它们重写了从`Animal`继承来的`move`方法,使得`move`方法根据不同的类而具有不同的功能。 注意,即使`tom`被声明为`Animal`类型,但因为它的值是`Horse`,调用`tom.move(34)`时,它会调用`Horse`里重写的方法: @@ -108,17 +114,19 @@ Tommy the Palomino moved 34m. ### 默认为`public` -在上面的例子里,我们可以自由的访问程序里定义的成员。 如果你对其它语言中的类比较了解,就会注意到我们在之前的代码里并没有使用`public`来做修饰;例如,C\#要求必须明确地使用`public`指定成员是可见的。 在TypeScript里,成员都默认为`public`。 +在上面的例子里,我们可以自由的访问程序里定义的成员。 如果你对其它语言中的类比较了解,就会注意到我们在之前的代码里并没有使用`public`来做修饰;例如,C\#要求必须明确地使用`public`指定成员是可见的。 在 TypeScript 里,成员都默认为`public`。 你也可以明确的将一个成员标记成`public`。 我们可以用下面的方式来重写上面的`Animal`类: ```typescript class Animal { - public name: string; - public constructor(theName: string) { this.name = theName; } - public move(distanceInMeters: number) { - console.log(`${this.name} moved ${distanceInMeters}m.`); - } + public name: string; + public constructor(theName: string) { + this.name = theName; + } + public move(distanceInMeters: number) { + console.log(`${this.name} moved ${distanceInMeters}m.`); + } } ``` @@ -128,14 +136,16 @@ class Animal { ```typescript class Animal { - private name: string; - constructor(theName: string) { this.name = theName; } + private name: string; + constructor(theName: string) { + this.name = theName; + } } -new Animal("Cat").name; // 错误: 'name' 是私有的. +new Animal('Cat').name; // 错误: 'name' 是私有的. ``` -TypeScript使用的是结构性类型系统。 当我们比较两种不同的类型时,并不在乎它们从何处而来,如果所有成员的类型都是兼容的,我们就认为它们的类型是兼容的。 +TypeScript 使用的是结构性类型系统。 当我们比较两种不同的类型时,并不在乎它们从何处而来,如果所有成员的类型都是兼容的,我们就认为它们的类型是兼容的。 然而,当我们比较带有`private`或`protected`成员的类型的时候,情况就不同了。 如果其中一个类型里包含一个`private`成员,那么只有当另外一个类型中也存在这样一个`private`成员, 并且它们都是来自同一处声明时,我们才认为这两个类型是兼容的。 对于`protected`成员也使用这个规则。 @@ -143,22 +153,28 @@ TypeScript使用的是结构性类型系统。 当我们比较两种不同的类 ```typescript class Animal { - private name: string; - constructor(theName: string) { this.name = theName; } + private name: string; + constructor(theName: string) { + this.name = theName; + } } class Rhino extends Animal { - constructor() { super("Rhino"); } + constructor() { + super('Rhino'); + } } class Employee { - private name: string; - constructor(theName: string) { this.name = theName; } + private name: string; + constructor(theName: string) { + this.name = theName; + } } -let animal = new Animal("Goat"); +let animal = new Animal('Goat'); let rhino = new Rhino(); -let employee = new Employee("Bob"); +let employee = new Employee('Bob'); animal = rhino; animal = employee; // 错误: Animal 与 Employee 不兼容. @@ -172,24 +188,26 @@ animal = employee; // 错误: Animal 与 Employee 不兼容. ```typescript class Person { - protected name: string; - constructor(name: string) { this.name = name; } + protected name: string; + constructor(name: string) { + this.name = name; + } } class Employee extends Person { - private department: string; + private department: string; - constructor(name: string, department: string) { - super(name) - this.department = department; - } + constructor(name: string, department: string) { + super(name); + this.department = department; + } - public getElevatorPitch() { - return `Hello, my name is ${this.name} and I work in ${this.department}.`; - } + public getElevatorPitch() { + return `Hello, my name is ${this.name} and I work in ${this.department}.`; + } } -let howard = new Employee("Howard", "Sales"); +let howard = new Employee('Howard', 'Sales'); console.log(howard.getElevatorPitch()); console.log(howard.name); // 错误 ``` @@ -200,54 +218,56 @@ console.log(howard.name); // 错误 ```typescript class Person { - protected name: string; - protected constructor(theName: string) { this.name = theName; } + protected name: string; + protected constructor(theName: string) { + this.name = theName; + } } // Employee 能够继承 Person class Employee extends Person { - private department: string; + private department: string; - constructor(name: string, department: string) { - super(name); - this.department = department; - } + constructor(name: string, department: string) { + super(name); + this.department = department; + } - public getElevatorPitch() { - return `Hello, my name is ${this.name} and I work in ${this.department}.`; - } + public getElevatorPitch() { + return `Hello, my name is ${this.name} and I work in ${this.department}.`; + } } -let howard = new Employee("Howard", "Sales"); -let john = new Person("John"); // 错误: 'Person' 的构造函数是被保护的. +let howard = new Employee('Howard', 'Sales'); +let john = new Person('John'); // 错误: 'Person' 的构造函数是被保护的. ``` -## readonly修饰符 +## readonly 修饰符 你可以使用`readonly`关键字将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化。 ```typescript class Octopus { - readonly name: string; - readonly numberOfLegs: number = 8; - constructor (theName: string) { - this.name = theName; - } + readonly name: string; + readonly numberOfLegs: number = 8; + constructor(theName: string) { + this.name = theName; + } } -let dad = new Octopus("Man with the 8 strong legs"); -dad.name = "Man with the 3-piece suit"; // 错误! name 是只读的. +let dad = new Octopus('Man with the 8 strong legs'); +dad.name = 'Man with the 3-piece suit'; // 错误! name 是只读的. ``` ### 参数属性 -在上面的例子中,我们不得不在在`Person`类里定义一个只读成员`name`和一个构造函数参数`theName`。这样做是为了在`Octopus`构造函数被执行后,就可以访问`theName`的值。 这种情况经常会遇到。_参数属性_可以方便地让我们在一个地方定义并初始化一个成员。 下面的例子是对之前`Animal`类的修改版,使用了参数属性: +在上面的例子中,我们不得不在在`Person`类里定义一个只读成员`name`和一个构造函数参数`theName`。这样做是为了在`Octopus`构造函数被执行后,就可以访问`theName`的值。 这种情况经常会遇到。*参数属性*可以方便地让我们在一个地方定义并初始化一个成员。 下面的例子是对之前`Animal`类的修改版,使用了参数属性: ```typescript class Animal { - constructor(private name: string) { } - move(distanceInMeters: number) { - console.log(`${this.name} moved ${distanceInMeters}m.`); - } + constructor(private name: string) {} + move(distanceInMeters: number) { + console.log(`${this.name} moved ${distanceInMeters}m.`); + } } ``` @@ -257,19 +277,19 @@ class Animal { ## 存取器 -TypeScript支持通过getters/setters来截取对对象成员的访问。 它能帮助你有效的控制对对象成员的访问。 +TypeScript 支持通过 getters/setters 来截取对对象成员的访问。 它能帮助你有效的控制对对象成员的访问。 下面来看如何把一个简单的类改写成使用`get`和`set`。 首先,我们从一个没有使用存取器的例子开始。 ```typescript class Employee { - fullName: string; + fullName: string; } let employee = new Employee(); -employee.fullName = "Bob Smith"; +employee.fullName = 'Bob Smith'; if (employee.fullName) { - console.log(employee.fullName); + console.log(employee.fullName); } ``` @@ -283,25 +303,25 @@ if (employee.fullName) { const fullNameMaxLength = 10; class Employee { - private _fullName: string; - - get fullName(): string { - return this._fullName; - } + private _fullName: string; - set fullName(newName: string) { - if (newName && newName.length > fullNameMaxLength) { - throw new Error("fullName has a max length of " + fullNameMaxLength); - } + get fullName(): string { + return this._fullName; + } - this._fullName = newName; + set fullName(newName: string) { + if (newName && newName.length > fullNameMaxLength) { + throw new Error('fullName has a max length of ' + fullNameMaxLength); } + + this._fullName = newName; + } } let employee = new Employee(); -employee.fullName = "Bob Smith"; +employee.fullName = 'Bob Smith'; if (employee.fullName) { - alert(employee.fullName); + alert(employee.fullName); } ``` @@ -309,7 +329,7 @@ if (employee.fullName) { 对于存取器有下面几点需要注意的: -首先,存取器要求你将编译器设置为输出ECMAScript 5或更高。 不支持降级到ECMAScript 3。 其次,只带有`get`不带有`set`的存取器自动被推断为`readonly`。 这在从代码生成`.d.ts`文件时是有帮助的,因为利用这个属性的用户会看到不允许够改变它的值。 +首先,存取器要求你将编译器设置为输出 ECMAScript 5 或更高。 不支持降级到 ECMAScript 3。 其次,只带有`get`不带有`set`的存取器自动被推断为`readonly`。 这在从代码生成`.d.ts`文件时是有帮助的,因为利用这个属性的用户会看到不允许够改变它的值。 ## 静态属性 @@ -317,20 +337,20 @@ if (employee.fullName) { ```typescript class Grid { - static origin = {x: 0, y: 0}; - calculateDistanceFromOrigin(point: {x: number; y: number;}) { - let xDist = (point.x - Grid.origin.x); - let yDist = (point.y - Grid.origin.y); - return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale; - } - constructor (public scale: number) { } + static origin = { x: 0, y: 0 }; + calculateDistanceFromOrigin(point: { x: number; y: number }) { + let xDist = point.x - Grid.origin.x; + let yDist = point.y - Grid.origin.y; + return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale; + } + constructor(public scale: number) {} } -let grid1 = new Grid(1.0); // 1x scale -let grid2 = new Grid(5.0); // 5x scale +let grid1 = new Grid(1.0); // 1x scale +let grid2 = new Grid(5.0); // 5x scale -console.log(grid1.calculateDistanceFromOrigin({x: 10, y: 10})); -console.log(grid2.calculateDistanceFromOrigin({x: 10, y: 10})); +console.log(grid1.calculateDistanceFromOrigin({ x: 10, y: 10 })); +console.log(grid2.calculateDistanceFromOrigin({ x: 10, y: 10 })); ``` ## 抽象类 @@ -339,10 +359,10 @@ console.log(grid2.calculateDistanceFromOrigin({x: 10, y: 10})); ```typescript abstract class Animal { - abstract makeSound(): void; - move(): void { - console.log("roaming the earth..."); - } + abstract makeSound(): void; + move(): void { + console.log('roaming the earth...'); + } } ``` @@ -350,30 +370,27 @@ abstract class Animal { ```typescript abstract class Department { + constructor(public name: string) {} - constructor(public name: string) { - } - - printName(): void { - console.log('Department name: ' + this.name); - } + printName(): void { + console.log('Department name: ' + this.name); + } - abstract printMeeting(): void; // 必须在派生类中实现 + abstract printMeeting(): void; // 必须在派生类中实现 } class AccountingDepartment extends Department { + constructor() { + super('Accounting and Auditing'); // 在派生类的构造函数中必须调用 super() + } - constructor() { - super('Accounting and Auditing'); // 在派生类的构造函数中必须调用 super() - } + printMeeting(): void { + console.log('The Accounting Department meets each Monday at 10am.'); + } - printMeeting(): void { - console.log('The Accounting Department meets each Monday at 10am.'); - } - - generateReports(): void { - console.log('Generating accounting reports...'); - } + generateReports(): void { + console.log('Generating accounting reports...'); + } } let department: Department; // 允许创建一个对抽象类型的引用 @@ -388,60 +405,59 @@ department.generateReports(); // 错误: 方法在声明的抽象类中不存在 ### 构造函数 -当你在TypeScript里声明了一个类的时候,实际上同时声明了很多东西。 首先就是类的_实例_的类型。 +当你在 TypeScript 里声明了一个类的时候,实际上同时声明了很多东西。 首先就是类的*实例*的类型。 ```typescript class Greeter { - greeting: string; - constructor(message: string) { - this.greeting = message; - } - greet() { - return "Hello, " + this.greeting; - } + greeting: string; + constructor(message: string) { + this.greeting = message; + } + greet() { + return 'Hello, ' + this.greeting; + } } let greeter: Greeter; -greeter = new Greeter("world"); +greeter = new Greeter('world'); console.log(greeter.greet()); ``` 这里,我们写了`let greeter: Greeter`,意思是`Greeter`类的实例的类型是`Greeter`。 这对于用过其它面向对象语言的程序员来讲已经是老习惯了。 -我们也创建了一个叫做_构造函数_的值。 这个函数会在我们使用`new`创建类实例的时候被调用。 下面我们来看看,上面的代码被编译成JavaScript后是什么样子的: +我们也创建了一个叫做*构造函数*的值。 这个函数会在我们使用`new`创建类实例的时候被调用。 下面我们来看看,上面的代码被编译成 JavaScript 后是什么样子的: ```typescript let Greeter = (function () { - function Greeter(message) { - this.greeting = message; - } - Greeter.prototype.greet = function () { - return "Hello, " + this.greeting; - }; - return Greeter; + function Greeter(message) { + this.greeting = message; + } + Greeter.prototype.greet = function () { + return 'Hello, ' + this.greeting; + }; + return Greeter; })(); let greeter; -greeter = new Greeter("world"); +greeter = new Greeter('world'); console.log(greeter.greet()); ``` -上面的代码里,`let Greeter`将被赋值为构造函数。 当我们调用`new`并执行了这个函数后,便会得到一个类的实例。 这个构造函数也包含了类的所有静态属性。 换个角度说,我们可以认为类具有_实例部分_与_静态部分_这两个部分。 +上面的代码里,`let Greeter`将被赋值为构造函数。 当我们调用`new`并执行了这个函数后,便会得到一个类的实例。 这个构造函数也包含了类的所有静态属性。 换个角度说,我们可以认为类具有*实例部分*与*静态部分*这两个部分。 让我们稍微改写一下这个例子,看看它们之间的区别: ```typescript class Greeter { - static standardGreeting = "Hello, there"; - greeting: string; - greet() { - if (this.greeting) { - return "Hello, " + this.greeting; - } - else { - return Greeter.standardGreeting; - } + static standardGreeting = 'Hello, there'; + greeting: string; + greet() { + if (this.greeting) { + return 'Hello, ' + this.greeting; + } else { + return Greeter.standardGreeting; } + } } let greeter1: Greeter; @@ -449,7 +465,7 @@ greeter1 = new Greeter(); console.log(greeter1.greet()); let greeterMaker: typeof Greeter = Greeter; -greeterMaker.standardGreeting = "Hey there!"; +greeterMaker.standardGreeting = 'Hey there!'; let greeter2: Greeter = new greeterMaker(); console.log(greeter2.greet()); @@ -457,7 +473,7 @@ console.log(greeter2.greet()); 这个例子里,`greeter1`与之前看到的一样。 我们实例化`Greeter`类,并使用这个对象。 与我们之前看到的一样。 -再之后,我们直接使用类。 我们创建了一个叫做`greeterMaker`的变量。 这个变量保存了这个类或者说保存了类构造函数。 然后我们使用`typeof Greeter`,意思是取Greeter类的类型,而不是实例的类型。 或者更确切的说,"告诉我`Greeter`标识符的类型",也就是构造函数的类型。 这个类型包含了类的所有静态成员和构造函数。 之后,就和前面一样,我们在`greeterMaker`上使用`new`,创建`Greeter`的实例。 +再之后,我们直接使用类。 我们创建了一个叫做`greeterMaker`的变量。 这个变量保存了这个类或者说保存了类构造函数。 然后我们使用`typeof Greeter`,意思是取 Greeter 类的类型,而不是实例的类型。 或者更确切的说,"告诉我`Greeter`标识符的类型",也就是构造函数的类型。 这个类型包含了类的所有静态成员和构造函数。 之后,就和前面一样,我们在`greeterMaker`上使用`new`,创建`Greeter`的实例。 ### 把类当做接口使用 @@ -465,14 +481,13 @@ console.log(greeter2.greet()); ```typescript class Point { - x: number; - y: number; + x: number; + y: number; } interface Point3d extends Point { - z: number; + z: number; } -let point3d: Point3d = {x: 1, y: 2, z: 3}; +let point3d: Point3d = { x: 1, y: 2, z: 3 }; ``` - diff --git a/zh/handbook/enums.md b/zh/handbook/enums.md index e6189fa1..4a60008e 100644 --- a/zh/handbook/enums.md +++ b/zh/handbook/enums.md @@ -2,7 +2,7 @@ ## 枚举 -使用枚举我们可以定义一些带名字的常量。 使用枚举可以清晰地表达意图或创建一组有区别的用例。 TypeScript支持数字的和基于字符串的枚举。 +使用枚举我们可以定义一些带名字的常量。 使用枚举可以清晰地表达意图或创建一组有区别的用例。 TypeScript 支持数字的和基于字符串的枚举。 ### 数字枚举 @@ -10,10 +10,10 @@ ```typescript enum Direction { - Up = 1, - Down, - Left, - Right + Up = 1, + Down, + Left, + Right, } ``` @@ -23,10 +23,10 @@ enum Direction { ```typescript enum Direction { - Up, - Down, - Left, - Right, + Up, + Down, + Left, + Right, } ``` @@ -36,23 +36,23 @@ enum Direction { ```typescript enum Response { - No = 0, - Yes = 1, + No = 0, + Yes = 1, } function respond(recipient: string, message: Response): void { - // ... + // ... } -respond("Princess Caroline", Response.Yes) +respond('Princess Caroline', Response.Yes); ``` 数字枚举可以被混入到[计算过的和常量成员(如下所示)](enums.md#computed-and-constant-members)。 简短地说,没有初始化器的成员要么在首位,要么必须在用数值常量或其他常量枚举成员初始化的数值枚举之后。 换句话说,下面的情况是不被允许的: ```typescript enum E { - A = getSomeValue(), - B, // Error! Enum member must have initializer. + A = getSomeValue(), + B, // Error! Enum member must have initializer. } ``` @@ -62,10 +62,10 @@ enum E { ```typescript enum Direction { - Up = "UP", - Down = "DOWN", - Left = "LEFT", - Right = "RIGHT", + Up = 'UP', + Down = 'DOWN', + Left = 'LEFT', + Right = 'RIGHT', } ``` @@ -77,37 +77,45 @@ enum Direction { ```typescript enum BooleanLikeHeterogeneousEnum { - No = 0, - Yes = "YES", + No = 0, + Yes = 'YES', } ``` -除非你真的想要利用JavaScript运行时的行为,否则我们不建议这样做。 +除非你真的想要利用 JavaScript 运行时的行为,否则我们不建议这样做。 ### 计算的和常量成员 -每个枚举成员都带有一个值,它可以是_常量_或_计算出来的_。 当满足如下条件时,枚举成员被当作是常量: +每个枚举成员都带有一个值,它可以是*常量*或*计算出来的*。 当满足如下条件时,枚举成员被当作是常量: -* 它是枚举的第一个成员且没有初始化器,这种情况下它被赋予值`0`: +- 它是枚举的第一个成员且没有初始化器,这种情况下它被赋予值`0`: ```typescript // E.X is constant: - enum E { X } + enum E { + X, + } ``` -* 它不带有初始化器且它之前的枚举成员是一个_数字_常量。 这种情况下,当前枚举成员的值为它上一个枚举成员的值加1。 +- 它不带有初始化器且它之前的枚举成员是一个*数字*常量。 这种情况下,当前枚举成员的值为它上一个枚举成员的值加 1。 ```typescript // All enum members in 'E1' and 'E2' are constant. - enum E1 { X, Y, Z } + enum E1 { + X, + Y, + Z, + } enum E2 { - A = 1, B, C + A = 1, + B, + C, } ``` -* 枚举成员使用_常量枚举表达式_初始化。 常量枚举表达式是TypeScript表达式的子集,它可以在编译阶段求值。 当一个表达式满足下面条件之一时,它就是一个常量枚举表达式: +- 枚举成员使用*常量枚举表达式*初始化。 常量枚举表达式是 TypeScript 表达式的子集,它可以在编译阶段求值。 当一个表达式满足下面条件之一时,它就是一个常量枚举表达式: 1. 一个枚举表达式字面量(主要是字符串字面量或数字字面量) 2. 一个对之前定义的常量枚举成员的引用(可以是在不同的枚举类型中定义的) @@ -121,13 +129,13 @@ enum BooleanLikeHeterogeneousEnum { ```typescript enum FileAccess { - // constant members - None, - Read = 1 << 1, - Write = 1 << 2, - ReadWrite = Read | Write, - // computed member - G = "123".length + // constant members + None, + Read = 1 << 1, + Write = 1 << 2, + ReadWrite = Read | Write, + // computed member + G = '123'.length, } ``` @@ -135,53 +143,53 @@ enum FileAccess { 存在一种特殊的非计算的常量枚举成员的子集:字面量枚举成员。 字面量枚举成员是指不带有初始值的常量枚举成员,或者是值被初始化为 -* 任何字符串字面量(例如:`"foo"`,`"bar"`,`"baz"`) -* 任何数字字面量(例如:`1`, `100`) -* 应用了一元`-`符号的数字字面量(例如:`-1`, `-100`) +- 任何字符串字面量(例如:`"foo"`,`"bar"`,`"baz"`) +- 任何数字字面量(例如:`1`, `100`) +- 应用了一元`-`符号的数字字面量(例如:`-1`, `-100`) 当所有枚举成员都拥有字面量枚举值时,它就带有了一种特殊的语义。 -首先,枚举成员成为了类型! 例如,我们可以说某些成员_只能_是枚举成员的值: +首先,枚举成员成为了类型! 例如,我们可以说某些成员*只能*是枚举成员的值: ```typescript enum ShapeKind { - Circle, - Square, + Circle, + Square, } interface Circle { - kind: ShapeKind.Circle; - radius: number; + kind: ShapeKind.Circle; + radius: number; } interface Square { - kind: ShapeKind.Square; - sideLength: number; + kind: ShapeKind.Square; + sideLength: number; } let c: Circle = { - kind: ShapeKind.Square, // Error! Type 'ShapeKind.Square' is not assignable to type 'ShapeKind.Circle'. - radius: 100, -} + kind: ShapeKind.Square, // Error! Type 'ShapeKind.Square' is not assignable to type 'ShapeKind.Circle'. + radius: 100, +}; ``` -另一个变化是枚举类型本身变成了每个枚举成员的_联合_。 虽然我们还没有讨论[联合类型](advanced-types.md#union-types),但你只要知道通过联合枚举,类型系统能够利用这样一个事实,它可以知道枚举里的值的集合。 因此,TypeScript能够捕获在比较值的时候犯的愚蠢的错误。 例如: +另一个变化是枚举类型本身变成了每个枚举成员的*联合*。 虽然我们还没有讨论[联合类型](advanced-types.md#union-types),但你只要知道通过联合枚举,类型系统能够利用这样一个事实,它可以知道枚举里的值的集合。 因此,TypeScript 能够捕获在比较值的时候犯的愚蠢的错误。 例如: ```typescript enum E { - Foo, - Bar, + Foo, + Bar, } function f(x: E) { - if (x !== E.Foo || x !== E.Bar) { - // ~~~~~~~~~~~ - // Error! This condition will always return 'true' since the types 'E.Foo' and 'E.Bar' have no overlap. - } + if (x !== E.Foo || x !== E.Bar) { + // ~~~~~~~~~~~ + // Error! This condition will always return 'true' since the types 'E.Foo' and 'E.Bar' have no overlap. + } } ``` -这个例子里,我们先检查`x`是否不是`E.Foo`。 如果通过了这个检查,然后`||`会发生短路效果,`if`语句体里的内容会被执行。 然而,这个检查没有通过,那么`x`则_只能_为`E.Foo`,因此没理由再去检查它是否为`E.Bar`。 +这个例子里,我们先检查`x`是否不是`E.Foo`。 如果通过了这个检查,然后`||`会发生短路效果,`if`语句体里的内容会被执行。 然而,这个检查没有通过,那么`x`则*只能*为`E.Foo`,因此没理由再去检查它是否为`E.Bar`。 ### 运行时的枚举 @@ -189,7 +197,9 @@ function f(x: E) { ```typescript enum E { - X, Y, Z + X, + Y, + Z, } ``` @@ -197,7 +207,7 @@ enum E { ```typescript function f(obj: { X: number }) { - return obj.X; + return obj.X; } // 没问题,因为 'E'包含一个数值型属性'X'。 @@ -210,7 +220,10 @@ f(E); ```typescript enum LogLevel { - ERROR, WARN, INFO, DEBUG + ERROR, + WARN, + INFO, + DEBUG, } /** @@ -220,34 +233,34 @@ enum LogLevel { type LogLevelStrings = keyof typeof LogLevel; function printImportant(key: LogLevelStrings, message: string) { - const num = LogLevel[key]; - if (num <= LogLevel.WARN) { - console.log('Log level key is: ', key); - console.log('Log level value is: ', num); - console.log('Log level message is: ', message); - } + const num = LogLevel[key]; + if (num <= LogLevel.WARN) { + console.log('Log level key is: ', key); + console.log('Log level value is: ', num); + console.log('Log level message is: ', message); + } } printImportant('ERROR', 'This is a message'); ``` #### 反向映射 -除了创建一个以属性名做为对象成员的对象之外,数字枚举成员还具有了_反向映射_,从枚举值到枚举名字。 例如,在下面的例子中: +除了创建一个以属性名做为对象成员的对象之外,数字枚举成员还具有了*反向映射*,从枚举值到枚举名字。 例如,在下面的例子中: ```typescript enum Enum { - A + A, } let a = Enum.A; let nameOfA = Enum[a]; // "A" ``` -TypeScript可能会将这段代码编译为下面的JavaScript: +TypeScript 可能会将这段代码编译为下面的 JavaScript: ```javascript var Enum; (function (Enum) { - Enum[Enum["A"] = 0] = "A"; + Enum[(Enum['A'] = 0)] = 'A'; })(Enum || (Enum = {})); var a = Enum.A; var nameOfA = Enum[a]; // "A" @@ -255,7 +268,7 @@ var nameOfA = Enum[a]; // "A" 生成的代码中,枚举类型被编译成一个对象,它包含了正向映射(`name` -> `value`)和反向映射(`value` -> `name`)。 引用枚举成员总会生成为对属性访问并且永远也不会内联代码。 -要注意的是_不会_为字符串枚举成员生成反向映射。 +要注意的是*不会*为字符串枚举成员生成反向映射。 #### `const`枚举 @@ -263,8 +276,8 @@ var nameOfA = Enum[a]; // "A" ```typescript const enum Enum { - A = 1, - B = A * 2 + A = 1, + B = A * 2, } ``` @@ -272,13 +285,18 @@ const enum Enum { ```typescript const enum Directions { - Up, - Down, - Left, - Right + Up, + Down, + Left, + Right, } -let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right] +let directions = [ + Directions.Up, + Directions.Down, + Directions.Left, + Directions.Right, +]; ``` 生成后的代码为: @@ -293,11 +311,10 @@ var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */]; ```typescript declare enum Enum { - A = 1, - B, - C = 2 + A = 1, + B, + C = 2, } ``` 外部枚举和非外部枚举之间有一个重要的区别,在正常的枚举里,没有初始化方法的成员被当成常量成员。 对于非常量的外部枚举而言,没有初始化方法时被当做需要经过计算的。 - diff --git a/zh/handbook/functions.md b/zh/handbook/functions.md index 8ab6b372..1167b3c7 100644 --- a/zh/handbook/functions.md +++ b/zh/handbook/functions.md @@ -2,31 +2,33 @@ ## 介绍 -函数是JavaScript应用程序的基础。 它帮助你实现抽象层,模拟类,信息隐藏和模块。 在TypeScript里,虽然已经支持类,命名空间和模块,但函数仍然是主要的定义_行为_的地方。 TypeScript为JavaScript函数添加了额外的功能,让我们可以更容易地使用。 +函数是 JavaScript 应用程序的基础。 它帮助你实现抽象层,模拟类,信息隐藏和模块。 在 TypeScript 里,虽然已经支持类,命名空间和模块,但函数仍然是主要的定义*行为*的地方。 TypeScript 为 JavaScript 函数添加了额外的功能,让我们可以更容易地使用。 ## 函数 -和JavaScript一样,TypeScript函数可以创建有名字的函数和匿名函数。 你可以随意选择适合应用程序的方式,不论是定义一系列API函数还是只使用一次的函数。 +和 JavaScript 一样,TypeScript 函数可以创建有名字的函数和匿名函数。 你可以随意选择适合应用程序的方式,不论是定义一系列 API 函数还是只使用一次的函数。 -通过下面的例子可以迅速回想起这两种JavaScript中的函数: +通过下面的例子可以迅速回想起这两种 JavaScript 中的函数: ```typescript // Named function function add(x, y) { - return x + y; + return x + y; } // Anonymous function -let myAdd = function(x, y) { return x + y; }; +let myAdd = function (x, y) { + return x + y; +}; ``` -在JavaScript里,函数可以使用函数体外部的变量。 当函数这么做时,我们说它‘捕获’了这些变量。 至于为什么可以这样做以及其中的利弊超出了本文的范围,但是深刻理解这个机制对学习JavaScript和TypeScript会很有帮助。 +在 JavaScript 里,函数可以使用函数体外部的变量。 当函数这么做时,我们说它‘捕获’了这些变量。 至于为什么可以这样做以及其中的利弊超出了本文的范围,但是深刻理解这个机制对学习 JavaScript 和 TypeScript 会很有帮助。 ```typescript let z = 100; function addToZ(x, y) { - return x + y + z; + return x + y + z; } ``` @@ -38,108 +40,119 @@ function addToZ(x, y) { ```typescript function add(x: number, y: number): number { - return x + y; + return x + y; } -let myAdd = function(x: number, y: number): number { return x + y; }; +let myAdd = function (x: number, y: number): number { + return x + y; +}; ``` -我们可以给每个参数添加类型之后再为函数本身添加返回值类型。 TypeScript能够根据返回语句自动推断出返回值类型,因此我们通常省略它。 +我们可以给每个参数添加类型之后再为函数本身添加返回值类型。 TypeScript 能够根据返回语句自动推断出返回值类型,因此我们通常省略它。 ### 书写完整函数类型 现在我们已经为函数指定了类型,下面让我们写出函数的完整类型。 ```typescript -let myAdd: (x:number, y:number) => number = - function(x: number, y: number): number { return x + y; }; +let myAdd: (x: number, y: number) => number = function ( + x: number, + y: number +): number { + return x + y; +}; ``` 函数类型包含两部分:参数类型和返回值类型。 当写出完整函数类型的时候,这两部分都是需要的。 我们以参数列表的形式写出参数类型,为每个参数指定一个名字和类型。 这个名字只是为了增加可读性。 我们也可以这么写: ```typescript -let myAdd: (baseValue: number, increment: number) => number = - function(x: number, y: number): number { return x + y; }; +let myAdd: (baseValue: number, increment: number) => number = function ( + x: number, + y: number +): number { + return x + y; +}; ``` 只要参数类型是匹配的,那么就认为它是有效的函数类型,而不在乎参数名是否正确。 第二部分是返回值类型。 对于返回值,我们在函数和返回值类型之前使用\(`=>`\)符号,使之清晰明了。 如之前提到的,返回值类型是函数类型的必要部分,如果函数没有返回任何值,你也必须指定返回值类型为`void`而不能留空。 -函数的类型只是由参数类型和返回值组成的。 函数中使用的捕获变量不会体现在类型里。 实际上,这些变量是函数的隐藏状态并不是组成API的一部分。 +函数的类型只是由参数类型和返回值组成的。 函数中使用的捕获变量不会体现在类型里。 实际上,这些变量是函数的隐藏状态并不是组成 API 的一部分。 ### 推断类型 -尝试这个例子的时候,你会注意到,就算仅在等式的一侧带有类型,TypeScript编译器仍可正确识别类型: +尝试这个例子的时候,你会注意到,就算仅在等式的一侧带有类型,TypeScript 编译器仍可正确识别类型: ```typescript // myAdd has the full function type -let myAdd = function(x: number, y: number): number { return x + y; }; +let myAdd = function (x: number, y: number): number { + return x + y; +}; // The parameters `x` and `y` have the type number -let myAdd: (baseValue: number, increment: number) => number = - function(x, y) { return x + y; }; +let myAdd: (baseValue: number, increment: number) => number = function (x, y) { + return x + y; +}; ``` 这叫做“按上下文归类”,是类型推论的一种。 它帮助我们更好地为程序指定类型。 ## 可选参数和默认参数 -TypeScript里的每个函数参数都是必须的。 这不是指不能传递`null`或`undefined`作为参数,而是说编译器检查用户是否为每个参数都传入了值。 编译器还会假设只有这些参数会被传递进函数。 简短地说,传递给一个函数的参数个数必须与函数期望的参数个数一致。 +TypeScript 里的每个函数参数都是必须的。 这不是指不能传递`null`或`undefined`作为参数,而是说编译器检查用户是否为每个参数都传入了值。 编译器还会假设只有这些参数会被传递进函数。 简短地说,传递给一个函数的参数个数必须与函数期望的参数个数一致。 ```typescript function buildName(firstName: string, lastName: string) { - return firstName + " " + lastName; + return firstName + ' ' + lastName; } -let result1 = buildName("Bob"); // error, too few parameters -let result2 = buildName("Bob", "Adams", "Sr."); // error, too many parameters -let result3 = buildName("Bob", "Adams"); // ah, just right +let result1 = buildName('Bob'); // error, too few parameters +let result2 = buildName('Bob', 'Adams', 'Sr.'); // error, too many parameters +let result3 = buildName('Bob', 'Adams'); // ah, just right ``` -JavaScript里,每个参数都是可选的,可传可不传。 没传参的时候,它的值就是undefined。 在TypeScript里我们可以在参数名旁使用`?`实现可选参数的功能。 比如,我们想让last name是可选的: +JavaScript 里,每个参数都是可选的,可传可不传。 没传参的时候,它的值就是 undefined。 在 TypeScript 里我们可以在参数名旁使用`?`实现可选参数的功能。 比如,我们想让 last name 是可选的: ```typescript function buildName(firstName: string, lastName?: string) { - if (lastName) - return firstName + " " + lastName; - else - return firstName; + if (lastName) return firstName + ' ' + lastName; + else return firstName; } -let result1 = buildName("Bob"); // works correctly now -let result2 = buildName("Bob", "Adams", "Sr."); // error, too many parameters -let result3 = buildName("Bob", "Adams"); // ah, just right +let result1 = buildName('Bob'); // works correctly now +let result2 = buildName('Bob', 'Adams', 'Sr.'); // error, too many parameters +let result3 = buildName('Bob', 'Adams'); // ah, just right ``` -可选参数必须跟在必须参数后面。 如果上例我们想让first name是可选的,那么就必须调整它们的位置,把first name放在后面。 +可选参数必须跟在必须参数后面。 如果上例我们想让 first name 是可选的,那么就必须调整它们的位置,把 first name 放在后面。 -在TypeScript里,我们也可以为参数提供一个默认值当用户没有传递这个参数或传递的值是`undefined`时。 它们叫做有默认初始化值的参数。 让我们修改上例,把last name的默认值设置为`"Smith"`。 +在 TypeScript 里,我们也可以为参数提供一个默认值当用户没有传递这个参数或传递的值是`undefined`时。 它们叫做有默认初始化值的参数。 让我们修改上例,把 last name 的默认值设置为`"Smith"`。 ```typescript -function buildName(firstName: string, lastName = "Smith") { - return firstName + " " + lastName; +function buildName(firstName: string, lastName = 'Smith') { + return firstName + ' ' + lastName; } -let result1 = buildName("Bob"); // works correctly now, returns "Bob Smith" -let result2 = buildName("Bob", undefined); // still works, also returns "Bob Smith" -let result3 = buildName("Bob", "Adams", "Sr."); // error, too many parameters -let result4 = buildName("Bob", "Adams"); // ah, just right +let result1 = buildName('Bob'); // works correctly now, returns "Bob Smith" +let result2 = buildName('Bob', undefined); // still works, also returns "Bob Smith" +let result3 = buildName('Bob', 'Adams', 'Sr.'); // error, too many parameters +let result4 = buildName('Bob', 'Adams'); // ah, just right ``` 在所有必须参数后面的带默认初始化的参数都是可选的,与可选参数一样,在调用函数的时候可以省略。 也就是说可选参数与末尾的默认参数共享参数类型。 ```typescript function buildName(firstName: string, lastName?: string) { - // ... + // ... } ``` 和 ```typescript -function buildName(firstName: string, lastName = "Smith") { - // ... +function buildName(firstName: string, lastName = 'Smith') { + // ... } ``` @@ -148,28 +161,28 @@ function buildName(firstName: string, lastName = "Smith") { 与普通可选参数不同的是,带默认值的参数不需要放在必须参数的后面。 如果带默认值的参数出现在必须参数前面,用户必须明确的传入`undefined`值来获得默认值。 例如,我们重写最后一个例子,让`firstName`是带默认值的参数: ```typescript -function buildName(firstName = "Will", lastName: string) { - return firstName + " " + lastName; +function buildName(firstName = 'Will', lastName: string) { + return firstName + ' ' + lastName; } -let result1 = buildName("Bob"); // error, too few parameters -let result2 = buildName("Bob", "Adams", "Sr."); // error, too many parameters -let result3 = buildName("Bob", "Adams"); // okay and returns "Bob Adams" -let result4 = buildName(undefined, "Adams"); // okay and returns "Will Adams" +let result1 = buildName('Bob'); // error, too few parameters +let result2 = buildName('Bob', 'Adams', 'Sr.'); // error, too many parameters +let result3 = buildName('Bob', 'Adams'); // okay and returns "Bob Adams" +let result4 = buildName(undefined, 'Adams'); // okay and returns "Will Adams" ``` ## 剩余参数 -必要参数,默认参数和可选参数有个共同点:它们表示某一个参数。 有时,你想同时操作多个参数,或者你并不知道会有多少参数传递进来。 在JavaScript里,你可以使用`arguments`来访问所有传入的参数。 +必要参数,默认参数和可选参数有个共同点:它们表示某一个参数。 有时,你想同时操作多个参数,或者你并不知道会有多少参数传递进来。 在 JavaScript 里,你可以使用`arguments`来访问所有传入的参数。 -在TypeScript里,你可以把所有参数收集到一个变量里: +在 TypeScript 里,你可以把所有参数收集到一个变量里: ```typescript function buildName(firstName: string, ...restOfName: string[]) { - return firstName + " " + restOfName.join(" "); + return firstName + ' ' + restOfName.join(' '); } -let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie"); +let employeeName = buildName('Joseph', 'Samuel', 'Lucas', 'MacKinzie'); ``` 剩余参数会被当做个数不限的可选参数。 可以一个都没有,同样也可以有任意个。 编译器创建参数数组,名字是你在省略号(`...`)后面给定的名字,你可以在函数体内使用这个数组。 @@ -178,7 +191,7 @@ let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie"); ```typescript function buildName(firstName: string, ...restOfName: string[]) { - return firstName + " " + restOfName.join(" "); + return firstName + ' ' + restOfName.join(' '); } let buildNameFun: (fname: string, ...rest: string[]) => string = buildName; @@ -186,60 +199,60 @@ let buildNameFun: (fname: string, ...rest: string[]) => string = buildName; ## `this` -学习如何在JavaScript里正确使用`this`就好比一场成年礼。 由于TypeScript是JavaScript的超集,TypeScript程序员也需要弄清`this`工作机制并且当有bug的时候能够找出错误所在。 幸运的是,TypeScript能通知你错误地使用了`this`的地方。 如果你想了解JavaScript里的`this`是如何工作的,那么首先阅读Yehuda Katz写的[Understanding JavaScript Function Invocation and "this"](http://yehudakatz.com/2011/08/11/understanding-javascript-function-invocation-and-this/)。 Yehuda的文章详细的阐述了`this`的内部工作原理,因此我们这里只做简单介绍。 +学习如何在 JavaScript 里正确使用`this`就好比一场成年礼。 由于 TypeScript 是 JavaScript 的超集,TypeScript 程序员也需要弄清`this`工作机制并且当有 bug 的时候能够找出错误所在。 幸运的是,TypeScript 能通知你错误地使用了`this`的地方。 如果你想了解 JavaScript 里的`this`是如何工作的,那么首先阅读 Yehuda Katz 写的[Understanding JavaScript Function Invocation and "this"](http://yehudakatz.com/2011/08/11/understanding-javascript-function-invocation-and-this/)。 Yehuda 的文章详细的阐述了`this`的内部工作原理,因此我们这里只做简单介绍。 ### `this`和箭头函数 -JavaScript里,`this`的值在函数被调用的时候才会指定。 这是个既强大又灵活的特点,但是你需要花点时间弄清楚函数调用的上下文是什么。 但众所周知,这不是一件很简单的事,尤其是在返回一个函数或将函数当做参数传递的时候。 +JavaScript 里,`this`的值在函数被调用的时候才会指定。 这是个既强大又灵活的特点,但是你需要花点时间弄清楚函数调用的上下文是什么。 但众所周知,这不是一件很简单的事,尤其是在返回一个函数或将函数当做参数传递的时候。 下面看一个例子: ```typescript let deck = { - suits: ["hearts", "spades", "clubs", "diamonds"], - cards: Array(52), - createCardPicker: function() { - return function() { - let pickedCard = Math.floor(Math.random() * 52); - let pickedSuit = Math.floor(pickedCard / 13); - - return {suit: this.suits[pickedSuit], card: pickedCard % 13}; - } - } -} + suits: ['hearts', 'spades', 'clubs', 'diamonds'], + cards: Array(52), + createCardPicker: function () { + return function () { + let pickedCard = Math.floor(Math.random() * 52); + let pickedSuit = Math.floor(pickedCard / 13); + + return { suit: this.suits[pickedSuit], card: pickedCard % 13 }; + }; + }, +}; let cardPicker = deck.createCardPicker(); let pickedCard = cardPicker(); -alert("card: " + pickedCard.card + " of " + pickedCard.suit); +alert('card: ' + pickedCard.card + ' of ' + pickedCard.suit); ``` 可以看到`createCardPicker`是个函数,并且它又返回了一个函数。 如果我们尝试运行这个程序,会发现它并没有弹出对话框而是报错了。 因为`createCardPicker`返回的函数里的`this`被设置成了`window`而不是`deck`对象。 因为我们只是独立地调用了`cardPicker()`。 顶级的非方法式调用会将`this`视为`window`。 (注意:在严格模式下,`this`为`undefined`而不是`window`)。 -为了解决这个问题,我们可以在函数被返回时就绑好正确的`this`。 这样的话,无论之后怎么使用它,都会引用绑定的‘deck’对象。 我们需要改变函数表达式来使用ECMAScript 6箭头语法。 箭头函数能保存函数创建时的`this`值,而不是调用时的值: +为了解决这个问题,我们可以在函数被返回时就绑好正确的`this`。 这样的话,无论之后怎么使用它,都会引用绑定的‘deck’对象。 我们需要改变函数表达式来使用 ECMAScript 6 箭头语法。 箭头函数能保存函数创建时的`this`值,而不是调用时的值: ```typescript let deck = { - suits: ["hearts", "spades", "clubs", "diamonds"], - cards: Array(52), - createCardPicker: function() { - // NOTE: the line below is now an arrow function, allowing us to capture 'this' right here - return () => { - let pickedCard = Math.floor(Math.random() * 52); - let pickedSuit = Math.floor(pickedCard / 13); - - return {suit: this.suits[pickedSuit], card: pickedCard % 13}; - } - } -} + suits: ['hearts', 'spades', 'clubs', 'diamonds'], + cards: Array(52), + createCardPicker: function () { + // NOTE: the line below is now an arrow function, allowing us to capture 'this' right here + return () => { + let pickedCard = Math.floor(Math.random() * 52); + let pickedSuit = Math.floor(pickedCard / 13); + + return { suit: this.suits[pickedSuit], card: pickedCard % 13 }; + }; + }, +}; let cardPicker = deck.createCardPicker(); let pickedCard = cardPicker(); -alert("card: " + pickedCard.card + " of " + pickedCard.suit); +alert('card: ' + pickedCard.card + ' of ' + pickedCard.suit); ``` -更好事情是,TypeScript会警告你犯了一个错误,如果你给编译器设置了`--noImplicitThis`标记。 它会指出`this.suits[pickedSuit]`里的`this`的类型为`any`。 +更好事情是,TypeScript 会警告你犯了一个错误,如果你给编译器设置了`--noImplicitThis`标记。 它会指出`this.suits[pickedSuit]`里的`this`的类型为`any`。 ### `this`参数 @@ -247,7 +260,7 @@ alert("card: " + pickedCard.card + " of " + pickedCard.suit); ```typescript function f(this: void) { - // make sure `this` is unusable in this standalone function + // make sure `this` is unusable in this standalone function } ``` @@ -255,35 +268,35 @@ function f(this: void) { ```typescript interface Card { - suit: string; - card: number; + suit: string; + card: number; } interface Deck { - suits: string[]; - cards: number[]; - createCardPicker(this: Deck): () => Card; + suits: string[]; + cards: number[]; + createCardPicker(this: Deck): () => Card; } let deck: Deck = { - suits: ["hearts", "spades", "clubs", "diamonds"], - cards: Array(52), - // NOTE: The function now explicitly specifies that its callee must be of type Deck - createCardPicker: function(this: Deck) { - return () => { - let pickedCard = Math.floor(Math.random() * 52); - let pickedSuit = Math.floor(pickedCard / 13); - - return {suit: this.suits[pickedSuit], card: pickedCard % 13}; - } - } -} + suits: ['hearts', 'spades', 'clubs', 'diamonds'], + cards: Array(52), + // NOTE: The function now explicitly specifies that its callee must be of type Deck + createCardPicker: function (this: Deck) { + return () => { + let pickedCard = Math.floor(Math.random() * 52); + let pickedSuit = Math.floor(pickedCard / 13); + + return { suit: this.suits[pickedSuit], card: pickedCard % 13 }; + }; + }, +}; let cardPicker = deck.createCardPicker(); let pickedCard = cardPicker(); -alert("card: " + pickedCard.card + " of " + pickedCard.suit); +alert('card: ' + pickedCard.card + ' of ' + pickedCard.suit); ``` -现在TypeScript知道`createCardPicker`期望在某个`Deck`对象上调用。 也就是说`this`是`Deck`类型的,而非`any`,因此`--noImplicitThis`不会报错了。 +现在 TypeScript 知道`createCardPicker`期望在某个`Deck`对象上调用。 也就是说`this`是`Deck`类型的,而非`any`,因此`--noImplicitThis`不会报错了。 #### 回调函数里的`this`参数 @@ -291,7 +304,7 @@ alert("card: " + pickedCard.card + " of " + pickedCard.suit); ```typescript interface UIElement { - addClickListener(onclick: (this: void, e: Event) => void): void; + addClickListener(onclick: (this: void, e: Event) => void): void; } ``` @@ -299,25 +312,25 @@ interface UIElement { ```typescript class Handler { - info: string; - onClickBad(this: Handler, e: Event) { - // oops, used this here. using this callback would crash at runtime - this.info = e.message; - } + info: string; + onClickBad(this: Handler, e: Event) { + // oops, used this here. using this callback would crash at runtime + this.info = e.message; + } } let h = new Handler(); uiElement.addClickListener(h.onClickBad); // error! ``` -指定了`this`类型后,你显式声明`onClickBad`必须在`Handler`的实例上调用。 然后TypeScript会检测到`addClickListener`要求函数带有`this: void`。 改变`this`类型来修复这个错误: +指定了`this`类型后,你显式声明`onClickBad`必须在`Handler`的实例上调用。 然后 TypeScript 会检测到`addClickListener`要求函数带有`this: void`。 改变`this`类型来修复这个错误: ```typescript class Handler { - info: string; - onClickGood(this: void, e: Event) { - // can't use this here because it's of type void! - console.log('clicked!'); - } + info: string; + onClickGood(this: void, e: Event) { + // can't use this here because it's of type void! + console.log('clicked!'); + } } let h = new Handler(); uiElement.addClickListener(h.onClickGood); @@ -327,8 +340,10 @@ uiElement.addClickListener(h.onClickGood); ```typescript class Handler { - info: string; - onClickGood = (e: Event) => { this.info = e.message } + info: string; + onClickGood = (e: Event) => { + this.info = e.message; + }; } ``` @@ -336,31 +351,35 @@ class Handler { ## 重载 -JavaScript本身是个动态语言。 JavaScript里函数根据传入不同的参数而返回不同类型的数据是很常见的。 +JavaScript 本身是个动态语言。 JavaScript 里函数根据传入不同的参数而返回不同类型的数据是很常见的。 ```typescript -let suits = ["hearts", "spades", "clubs", "diamonds"]; +let suits = ['hearts', 'spades', 'clubs', 'diamonds']; function pickCard(x): any { - // Check to see if we're working with an object/array - // if so, they gave us the deck and we'll pick the card - if (typeof x == "object") { - let pickedCard = Math.floor(Math.random() * x.length); - return pickedCard; - } - // Otherwise just let them pick the card - else if (typeof x == "number") { - let pickedSuit = Math.floor(x / 13); - return { suit: suits[pickedSuit], card: x % 13 }; - } + // Check to see if we're working with an object/array + // if so, they gave us the deck and we'll pick the card + if (typeof x == 'object') { + let pickedCard = Math.floor(Math.random() * x.length); + return pickedCard; + } + // Otherwise just let them pick the card + else if (typeof x == 'number') { + let pickedSuit = Math.floor(x / 13); + return { suit: suits[pickedSuit], card: x % 13 }; + } } -let myDeck = [{ suit: "diamonds", card: 2 }, { suit: "spades", card: 10 }, { suit: "hearts", card: 4 }]; +let myDeck = [ + { suit: 'diamonds', card: 2 }, + { suit: 'spades', card: 10 }, + { suit: 'hearts', card: 4 }, +]; let pickedCard1 = myDeck[pickCard(myDeck)]; -alert("card: " + pickedCard1.card + " of " + pickedCard1.suit); +alert('card: ' + pickedCard1.card + ' of ' + pickedCard1.suit); let pickedCard2 = pickCard(15); -alert("card: " + pickedCard2.card + " of " + pickedCard2.suit); +alert('card: ' + pickedCard2.card + ' of ' + pickedCard2.suit); ``` `pickCard`方法根据传入参数的不同会返回两种不同的类型。 如果传入的是代表纸牌的对象,函数作用是从中抓一张牌。 如果用户想抓牌,我们告诉他抓到了什么牌。 但是这怎么在类型系统里表示呢。 @@ -368,35 +387,38 @@ alert("card: " + pickedCard2.card + " of " + pickedCard2.suit); 方法是为同一个函数提供多个函数类型定义来进行函数重载。 编译器会根据这个列表去处理函数的调用。 下面我们来重载`pickCard`函数。 ```typescript -let suits = ["hearts", "spades", "clubs", "diamonds"]; +let suits = ['hearts', 'spades', 'clubs', 'diamonds']; -function pickCard(x: {suit: string; card: number; }[]): number; -function pickCard(x: number): {suit: string; card: number; }; +function pickCard(x: { suit: string; card: number }[]): number; +function pickCard(x: number): { suit: string; card: number }; function pickCard(x): any { - // Check to see if we're working with an object/array - // if so, they gave us the deck and we'll pick the card - if (typeof x == "object") { - let pickedCard = Math.floor(Math.random() * x.length); - return pickedCard; - } - // Otherwise just let them pick the card - else if (typeof x == "number") { - let pickedSuit = Math.floor(x / 13); - return { suit: suits[pickedSuit], card: x % 13 }; - } + // Check to see if we're working with an object/array + // if so, they gave us the deck and we'll pick the card + if (typeof x == 'object') { + let pickedCard = Math.floor(Math.random() * x.length); + return pickedCard; + } + // Otherwise just let them pick the card + else if (typeof x == 'number') { + let pickedSuit = Math.floor(x / 13); + return { suit: suits[pickedSuit], card: x % 13 }; + } } -let myDeck = [{ suit: "diamonds", card: 2 }, { suit: "spades", card: 10 }, { suit: "hearts", card: 4 }]; +let myDeck = [ + { suit: 'diamonds', card: 2 }, + { suit: 'spades', card: 10 }, + { suit: 'hearts', card: 4 }, +]; let pickedCard1 = myDeck[pickCard(myDeck)]; -alert("card: " + pickedCard1.card + " of " + pickedCard1.suit); +alert('card: ' + pickedCard1.card + ' of ' + pickedCard1.suit); let pickedCard2 = pickCard(15); -alert("card: " + pickedCard2.card + " of " + pickedCard2.suit); +alert('card: ' + pickedCard2.card + ' of ' + pickedCard2.suit); ``` 这样改变后,重载的`pickCard`函数在调用的时候会进行正确的类型检查。 -为了让编译器能够选择正确的检查类型,它与JavaScript里的处理流程相似。 它查找重载列表,尝试使用第一个重载定义。 如果匹配的话就使用这个。 因此,在定义重载的时候,一定要把最精确的定义放在最前面。 +为了让编译器能够选择正确的检查类型,它与 JavaScript 里的处理流程相似。 它查找重载列表,尝试使用第一个重载定义。 如果匹配的话就使用这个。 因此,在定义重载的时候,一定要把最精确的定义放在最前面。 注意,`function pickCard(x): any`并不是重载列表的一部分,因此这里只有两个重载:一个是接收对象另一个接收数字。 以其它参数调用`pickCard`会产生错误。 - diff --git a/zh/handbook/generics.md b/zh/handbook/generics.md index c248fabb..07ff68e8 100644 --- a/zh/handbook/generics.md +++ b/zh/handbook/generics.md @@ -2,19 +2,21 @@ ## 介绍 -软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。 +软件工程中,我们不仅要创建一致的定义良好的 API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。 -在像C\#和Java这样的语言中,可以使用`泛型`来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。 +在像 C\#和 Java 这样的语言中,可以使用`泛型`来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。 -## 泛型之Hello World +## 泛型之 Hello World -下面来创建第一个使用泛型的例子:identity函数。 这个函数会返回任何传入它的值。 你可以把这个函数当成是`echo`命令。 +下面来创建第一个使用泛型的例子:identity 函数。 +这个函数会返回任何传入它的值。 +你可以把这个函数当成是`echo`命令。 不用泛型的话,这个函数可能是下面这样: ```typescript function identity(arg: number): number { - return arg; + return arg; } ``` @@ -22,39 +24,39 @@ function identity(arg: number): number { ```typescript function identity(arg: any): any { - return arg; + return arg; } ``` 使用`any`类型会导致这个函数可以接收任何类型的`arg`参数,这样就丢失了一些信息:传入的类型与返回的类型应该是相同的。 如果我们传入一个数字,我们只知道任何类型的值都有可能被返回。 -因此,我们需要一种方法使返回值的类型与传入参数的类型是相同的。 这里,我们使用了_类型变量_,它是一种特殊的变量,只用于表示类型而不是值。 +因此,我们需要一种方法使返回值的类型与传入参数的类型是相同的。 这里,我们使用了*类型变量*,它是一种特殊的变量,只用于表示类型而不是值。 ```typescript function identity(arg: T): T { - return arg; + return arg; } ``` -我们给identity添加了类型变量`T`。 `T`帮助我们捕获用户传入的类型(比如:`number`),之后我们就可以使用这个类型。 之后我们再次使用了`T`当做返回值类型。现在我们可以知道参数类型与返回值类型是相同的了。 这允许我们跟踪函数里使用的类型的信息。 +我们给 identity 添加了类型变量`T`。 `T`帮助我们捕获用户传入的类型(比如:`number`),之后我们就可以使用这个类型。 之后我们再次使用了`T`当做返回值类型。现在我们可以知道参数类型与返回值类型是相同的了。 这允许我们跟踪函数里使用的类型的信息。 我们把这个版本的`identity`函数叫做泛型,因为它可以适用于多个类型。 不同于使用`any`,它不会丢失信息,像第一个例子那像保持准确性,传入数值类型并返回数值类型。 我们定义了泛型函数后,可以用两种方法使用。 第一种是,传入所有的参数,包含类型参数: ```typescript -let output = identity("myString"); // type of output will be 'string' +let output = identity('myString'); // type of output will be 'string' ``` 这里我们明确的指定了`T`是`string`类型,并做为一个参数传给函数,使用了`<>`括起来而不是`()`。 -第二种方法更普遍。利用了_类型推论_ -- 即编译器会根据传入的参数自动地帮助我们确定T的类型: +第二种方法更普遍。利用了*类型推论* -- 即编译器会根据传入的参数自动地帮助我们确定 T 的类型: ```typescript -let output = identity("myString"); // type of output will be 'string' +let output = identity('myString'); // type of output will be 'string' ``` -注意我们没必要使用尖括号(`<>`)来明确地传入类型;编译器可以查看`myString`的值,然后把`T`设置为它的类型。 类型推论帮助我们保持代码精简和高可读性。如果编译器不能够自动地推断出类型的话,只能像上面那样明确的传入T的类型,在一些复杂的情况下,这是可能出现的。 +注意我们没必要使用尖括号(`<>`)来明确地传入类型;编译器可以查看`myString`的值,然后把`T`设置为它的类型。 类型推论帮助我们保持代码精简和高可读性。如果编译器不能够自动地推断出类型的话,只能像上面那样明确的传入 T 的类型,在一些复杂的情况下,这是可能出现的。 ## 使用泛型变量 @@ -64,7 +66,7 @@ let output = identity("myString"); // type of output will be 'string' ```typescript function identity(arg: T): T { - return arg; + return arg; } ``` @@ -72,8 +74,8 @@ function identity(arg: T): T { ```typescript function loggingIdentity(arg: T): T { - console.log(arg.length); // Error: T doesn't have .length - return arg; + console.log(arg.length); // Error: T doesn't have .length + return arg; } ``` @@ -83,19 +85,19 @@ function loggingIdentity(arg: T): T { ```typescript function loggingIdentity(arg: T[]): T[] { - console.log(arg.length); // Array has a .length, so no more error - return arg; + console.log(arg.length); // Array has a .length, so no more error + return arg; } ``` -你可以这样理解`loggingIdentity`的类型:泛型函数`loggingIdentity`,接收类型参数`T`和参数`arg`,它是个元素类型是`T`的数组,并返回元素类型是`T`的数组。 如果我们传入数字数组,将返回一个数字数组,因为此时`T`的的类型为`number`。 这可以让我们把泛型变量T当做类型的一部分使用,而不是整个类型,增加了灵活性。 +你可以这样理解`loggingIdentity`的类型:泛型函数`loggingIdentity`,接收类型参数`T`和参数`arg`,它是个元素类型是`T`的数组,并返回元素类型是`T`的数组。 如果我们传入数字数组,将返回一个数字数组,因为此时`T`的的类型为`number`。 这可以让我们把泛型变量 T 当做类型的一部分使用,而不是整个类型,增加了灵活性。 我们也可以这样实现上面的例子: ```typescript function loggingIdentity(arg: Array): Array { - console.log(arg.length); // Array has a .length, so no more error - return arg; + console.log(arg.length); // Array has a .length, so no more error + return arg; } ``` @@ -103,13 +105,13 @@ function loggingIdentity(arg: Array): Array { ## 泛型类型 -上一节,我们创建了identity通用函数,可以适用于不同的类型。 在这节,我们研究一下函数本身的类型,以及如何创建泛型接口。 +上一节,我们创建了 identity 通用函数,可以适用于不同的类型。 在这节,我们研究一下函数本身的类型,以及如何创建泛型接口。 泛型函数的类型与非泛型函数的类型没什么不同,只是有一个类型参数在最前面,像函数声明一样: ```typescript function identity(arg: T): T { - return arg; + return arg; } let myIdentity: (arg: T) => T = identity; @@ -119,7 +121,7 @@ let myIdentity: (arg: T) => T = identity; ```typescript function identity(arg: T): T { - return arg; + return arg; } let myIdentity: (arg: U) => U = identity; @@ -129,21 +131,21 @@ let myIdentity: (arg: U) => U = identity; ```typescript function identity(arg: T): T { - return arg; + return arg; } -let myIdentity: {(arg: T): T} = identity; +let myIdentity: { (arg: T): T } = identity; ``` 这引导我们去写第一个泛型接口了。 我们把上面例子里的对象字面量拿出来做为一个接口: ```typescript interface GenericIdentityFn { - (arg: T): T; + (arg: T): T; } function identity(arg: T): T { - return arg; + return arg; } let myIdentity: GenericIdentityFn = identity; @@ -153,11 +155,11 @@ let myIdentity: GenericIdentityFn = identity; ```typescript interface GenericIdentityFn { - (arg: T): T; + (arg: T): T; } function identity(arg: T): T { - return arg; + return arg; } let myIdentity: GenericIdentityFn = identity; @@ -173,23 +175,27 @@ let myIdentity: GenericIdentityFn = identity; ```typescript class GenericNumber { - zeroValue: T; - add: (x: T, y: T) => T; + zeroValue: T; + add: (x: T, y: T) => T; } let myGenericNumber = new GenericNumber(); myGenericNumber.zeroValue = 0; -myGenericNumber.add = function(x, y) { return x + y; }; +myGenericNumber.add = function (x, y) { + return x + y; +}; ``` `GenericNumber`类的使用是十分直观的,并且你可能已经注意到了,没有什么去限制它只能使用`number`类型。 也可以使用字符串或其它更复杂的类型。 ```typescript let stringNumeric = new GenericNumber(); -stringNumeric.zeroValue = ""; -stringNumeric.add = function(x, y) { return x + y; }; +stringNumeric.zeroValue = ''; +stringNumeric.add = function (x, y) { + return x + y; +}; -console.log(stringNumeric.add(stringNumeric.zeroValue, "test")); +console.log(stringNumeric.add(stringNumeric.zeroValue, 'test')); ``` 与接口一样,直接把泛型类型放在类后面,可以帮助我们确认类的所有属性都在使用相同的类型。 @@ -202,36 +208,36 @@ console.log(stringNumeric.add(stringNumeric.zeroValue, "test")); ```typescript function loggingIdentity(arg: T): T { - console.log(arg.length); // Error: T doesn't have .length - return arg; + console.log(arg.length); // Error: T doesn't have .length + return arg; } ``` -相比于操作any所有类型,我们想要限制函数去处理任意带有`.length`属性的所有类型。 只要传入的类型有这个属性,我们就允许,就是说至少包含这一属性。 为此,我们需要列出对于T的约束要求。 +相比于操作 any 所有类型,我们想要限制函数去处理任意带有`.length`属性的所有类型。 只要传入的类型有这个属性,我们就允许,就是说至少包含这一属性。 为此,我们需要列出对于 T 的约束要求。 为此,我们定义一个接口来描述约束条件。 创建一个包含`.length`属性的接口,使用这个接口和`extends`关键字来实现约束: ```typescript interface Lengthwise { - length: number; + length: number; } function loggingIdentity(arg: T): T { - console.log(arg.length); // Now we know it has a .length property, so no more error - return arg; + console.log(arg.length); // Now we know it has a .length property, so no more error + return arg; } ``` 现在这个泛型函数被定义了约束,因此它不再是适用于任意类型: ```typescript -loggingIdentity(3); // Error, number doesn't have a .length property +loggingIdentity(3); // Error, number doesn't have a .length property ``` 我们需要传入符合约束类型的值,必须包含必须的属性: ```typescript -loggingIdentity({length: 10, value: 3}); +loggingIdentity({ length: 10, value: 3 }); ``` ### 在泛型约束中使用类型参数 @@ -240,22 +246,22 @@ loggingIdentity({length: 10, value: 3}); ```typescript function getProperty(obj: T, key: K) { - return obj[key]; + return obj[key]; } let x = { a: 1, b: 2, c: 3, d: 4 }; -getProperty(x, "a"); // okay -getProperty(x, "m"); // error: Argument of type 'm' isn't assignable to 'a' | 'b' | 'c' | 'd'. +getProperty(x, 'a'); // okay +getProperty(x, 'm'); // error: Argument of type 'm' isn't assignable to 'a' | 'b' | 'c' | 'd'. ``` ### 在泛型里使用类类型 -在TypeScript使用泛型创建工厂函数时,需要引用构造函数的类类型。比如, +在 TypeScript 使用泛型创建工厂函数时,需要引用构造函数的类类型。比如, ```typescript -function create(c: {new(): T; }): T { - return new c(); +function create(c: { new (): T }): T { + return new c(); } ``` @@ -263,30 +269,29 @@ function create(c: {new(): T; }): T { ```typescript class BeeKeeper { - hasMask: boolean; + hasMask: boolean; } class ZooKeeper { - nametag: string; + nametag: string; } class Animal { - numLegs: number; + numLegs: number; } class Bee extends Animal { - keeper: BeeKeeper; + keeper: BeeKeeper; } class Lion extends Animal { - keeper: ZooKeeper; + keeper: ZooKeeper; } function createInstance(c: new () => A): A { - return new c(); + return new c(); } -createInstance(Lion).keeper.nametag; // typechecks! -createInstance(Bee).keeper.hasMask; // typechecks! +createInstance(Lion).keeper.nametag; // typechecks! +createInstance(Bee).keeper.hasMask; // typechecks! ``` - diff --git a/zh/handbook/interfaces.md b/zh/handbook/interfaces.md index d012cc10..963b417e 100644 --- a/zh/handbook/interfaces.md +++ b/zh/handbook/interfaces.md @@ -2,7 +2,7 @@ ## 介绍 -TypeScript 的核心原则之一是对值所具有的_结构_进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。 在 TypeScript 里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。 +TypeScript 的核心原则之一是对值所具有的*结构*进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。 在 TypeScript 里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。 ## 接口初探 @@ -13,7 +13,7 @@ function printLabel(labeledObj: { label: string }) { console.log(labeledObj.label); } -let myObj = { size: 10, label: "Size 10 Object" }; +let myObj = { size: 10, label: 'Size 10 Object' }; printLabel(myObj); ``` @@ -30,7 +30,7 @@ function printLabel(labeledObj: LabeledValue) { console.log(labeledObj.label); } -let myObj = { size: 10, label: "Size 10 Object" }; +let myObj = { size: 10, label: 'Size 10 Object' }; printLabel(myObj); ``` @@ -51,7 +51,7 @@ interface SquareConfig { } function createSquare(config: SquareConfig): { color: string; area: number } { - let newSquare = { color: "white", area: 100 }; + let newSquare = { color: 'white', area: 100 }; if (config.color) { newSquare.color = config.color; } @@ -61,7 +61,7 @@ function createSquare(config: SquareConfig): { color: string; area: number } { return newSquare; } -let mySquare = createSquare({ color: "black" }); +let mySquare = createSquare({ color: 'black' }); ``` 带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个`?`符号。 @@ -75,7 +75,7 @@ interface SquareConfig { } function createSquare(config: SquareConfig): { color: string; area: number } { - let newSquare = { color: "white", area: 100 }; + let newSquare = { color: 'white', area: 100 }; if (config.clor) { // Error: Property 'clor' does not exist on type 'SquareConfig' newSquare.color = config.clor; @@ -86,7 +86,7 @@ function createSquare(config: SquareConfig): { color: string; area: number } { return newSquare; } -let mySquare = createSquare({ color: "black" }); +let mySquare = createSquare({ color: 'black' }); ``` ## 只读属性 @@ -144,18 +144,18 @@ function createSquare(config: SquareConfig): { color: string; area: number } { // ... } -let mySquare = createSquare({ colour: "red", width: 100 }); +let mySquare = createSquare({ colour: 'red', width: 100 }); ``` 注意传入`createSquare`的参数拼写为`colour`而不是`color`。 在 JavaScript 里,这会默默地失败。 你可能会争辩这个程序已经正确地类型化了,因为`width`属性是兼容的,不存在`color`属性,而且额外的`colour`属性是无意义的。 -然而,TypeScript 会认为这段代码可能存在 bug。 对象字面量会被特殊对待而且会经过_额外属性检查_,当将它们赋值给变量或作为参数传递的时候。 如果一个对象字面量存在任何“目标类型”不包含的属性时,你会得到一个错误。 +然而,TypeScript 会认为这段代码可能存在 bug。 对象字面量会被特殊对待而且会经过*额外属性检查*,当将它们赋值给变量或作为参数传递的时候。 如果一个对象字面量存在任何“目标类型”不包含的属性时,你会得到一个错误。 ```typescript // error: Object literal may only specify known properties, but 'colour' does not exist in type 'SquareConfig'. Did you mean to write 'color'? -let mySquare = createSquare({ colour: "red", width: 100 }); +let mySquare = createSquare({ colour: 'red', width: 100 }); ``` 绕开这些检查非常简单。 最简便的方法是使用类型断言: @@ -164,7 +164,7 @@ let mySquare = createSquare({ colour: "red", width: 100 }); let mySquare = createSquare({ width: 100, opacity: 0.5 } as SquareConfig); ``` -然而,最佳的方式是能够添加一个字符串索引签名,前提是你能够确定这个对象可能具有某些做为特殊用途使用的额外属性。 如果`SquareConfig`带有上面定义的类型的`color`和`width`属性,并且_还会_带有任意数量的其它属性,那么我们可以这样定义它: +然而,最佳的方式是能够添加一个字符串索引签名,前提是你能够确定这个对象可能具有某些做为特殊用途使用的额外属性。 如果`SquareConfig`带有上面定义的类型的`color`和`width`属性,并且*还会*带有任意数量的其它属性,那么我们可以这样定义它: ```typescript interface SquareConfig { @@ -179,14 +179,14 @@ interface SquareConfig { 还有最后一种跳过这些检查的方式,这可能会让你感到惊讶,它就是将这个对象赋值给一个另一个变量: 因为`squareOptions`不会经过额外属性检查,所以编译器不会报错。 ```typescript -let squareOptions = { colour: "red", width: 100 }; +let squareOptions = { colour: 'red', width: 100 }; let mySquare = createSquare(squareOptions); ``` 上面的方法只在`squareOptions`和`SquareConfig`之间有共同的属性时才好用。 在这个例子中,这个属性为`width`。如果变量间不存在共同的对象属性将会报错。例如: ```typescript -let squareOptions = { colour: "red" }; +let squareOptions = { colour: 'red' }; let mySquare = createSquare(squareOptions); ``` @@ -208,7 +208,7 @@ interface SearchFunc { ```typescript let mySearch: SearchFunc; -mySearch = function(source: string, subString: string) { +mySearch = function (source: string, subString: string) { let result = source.search(subString); return result > -1; }; @@ -218,7 +218,7 @@ mySearch = function(source: string, subString: string) { ```typescript let mySearch: SearchFunc; -mySearch = function(src: string, sub: string): boolean { +mySearch = function (src: string, sub: string): boolean { let result = src.search(sub); return result > -1; }; @@ -228,7 +228,7 @@ mySearch = function(src: string, sub: string): boolean { ```typescript let mySearch: SearchFunc; -mySearch = function(src, sub) { +mySearch = function (src, sub) { let result = src.search(sub); return result > -1; }; @@ -241,15 +241,15 @@ let mySearch: SearchFunc; // error: Type '(src: string, sub: string) => string' is not assignable to type 'SearchFunc'. // Type 'string' is not assignable to type 'boolean'. -mySearch = function(src, sub) { +mySearch = function (src, sub) { let result = src.search(sub); - return "string"; + return 'string'; }; ``` ## 可索引的类型 -与使用接口描述函数类型差不多,我们也可以描述那些能够“通过索引得到”的类型,比如`a[10]`或`ageMap["daniel"]`。 可索引类型具有一个_索引签名_,它描述了对象索引的类型,还有相应的索引返回值类型。 让我们看一个例子: +与使用接口描述函数类型差不多,我们也可以描述那些能够“通过索引得到”的类型,比如`a[10]`或`ageMap["daniel"]`。 可索引类型具有一个*索引签名*,它描述了对象索引的类型,还有相应的索引返回值类型。 让我们看一个例子: ```typescript interface StringArray { @@ -257,7 +257,7 @@ interface StringArray { } let myArray: StringArray; -myArray = ["Bob", "Fred"]; +myArray = ['Bob', 'Fred']; let myStr: string = myArray[0]; ``` @@ -295,9 +295,9 @@ interface NumberDictionary { ```typescript interface NumberOrStringDictionary { - [index: string]: number | string; - length: number; // ok, length is a number - name: string; // ok, name is a string + [index: string]: number | string; + length: number; // ok, length is a number + name: string; // ok, name is a string } ``` @@ -307,8 +307,8 @@ interface NumberOrStringDictionary { interface ReadonlyStringArray { readonly [index: number]: string; } -let myArray: ReadonlyStringArray = ["Alice", "Bob"]; -myArray[2] = "Mallory"; // error! +let myArray: ReadonlyStringArray = ['Alice', 'Bob']; +myArray[2] = 'Mallory'; // error! ``` 你不能设置`myArray[2]`,因为索引签名是只读的。 @@ -387,13 +387,13 @@ function createClock( class DigitalClock implements ClockInterface { constructor(h: number, m: number) {} tick() { - console.log("beep beep"); + console.log('beep beep'); } } class AnalogClock implements ClockInterface { constructor(h: number, m: number) {} tick() { - console.log("tick tock"); + console.log('tick tock'); } } @@ -417,7 +417,7 @@ interface ClockInterface { const Clock: ClockConstructor = class Clock implements ClockInterface { constructor(h: number, m: number) {} tick() { - console.log("beep beep"); + console.log('beep beep'); } }; ``` @@ -436,7 +436,7 @@ interface Square extends Shape { } let square = {} as Square; -square.color = "blue"; +square.color = 'blue'; square.sideLength = 10; ``` @@ -456,7 +456,7 @@ interface Square extends Shape, PenStroke { } let square = {} as Square; -square.color = "blue"; +square.color = 'blue'; square.sideLength = 10; square.penWidth = 5.0; ``` @@ -475,9 +475,9 @@ interface Counter { } function getCounter(): Counter { - let counter = function(start: number) {} as Counter; + let counter = function (start: number) {} as Counter; counter.interval = 123; - counter.reset = function() {}; + counter.reset = function () {}; return counter; } @@ -513,8 +513,8 @@ class TextBox extends Control { } class ImageControl implements SelectableControl { -// Error: Class 'ImageControl' incorrectly implements interface 'SelectableControl'. -// Types have separate declarations of a private property 'state'. + // Error: Class 'ImageControl' incorrectly implements interface 'SelectableControl'. + // Types have separate declarations of a private property 'state'. private state: any; select() {} } @@ -523,4 +523,3 @@ class ImageControl implements SelectableControl { 在上面的例子里,`SelectableControl`包含了`Control`的所有成员,包括私有成员`state`。 因为`state`是私有成员,所以只能够是`Control`的子类们才能实现`SelectableControl`接口。 因为只有`Control`的子类才能够拥有一个声明于`Control`的私有成员`state`,这对私有成员的兼容性是必需的。 在`Control`类内部,是允许通过`SelectableControl`的实例来访问私有成员`state`的。 实际上,`SelectableControl`就像`Control`一样,并拥有一个`select`方法。 `Button`和`TextBox`类是`SelectableControl`的子类(因为它们都继承自`Control`并有`select`方法)。而对于 `ImageControl` 类,它有自身的私有成员 `state` 而不是通过继承 `Control` 得来的,所以它不可以实现 `SelectableControl` 。 - diff --git a/zh/handbook/literal-types.md b/zh/handbook/literal-types.md index d3e27d1c..5d757b8e 100644 --- a/zh/handbook/literal-types.md +++ b/zh/handbook/literal-types.md @@ -109,4 +109,3 @@ type ValidationResult = | ValidationSuccess | ValidationFailure; ``` - diff --git a/zh/javascript/type-checking-javascript-files.md b/zh/javascript/type-checking-javascript-files.md index f713e256..5a61e9aa 100644 --- a/zh/javascript/type-checking-javascript-files.md +++ b/zh/javascript/type-checking-javascript-files.md @@ -785,4 +785,3 @@ var normal; ``` 不同于JSDoc类型系统,TypeScript只允许将类型标记为包不包含`null`。 没有明确的`Non-nullable` -- 如果启用了`strictNullChecks`,那么`number`是非`null`的。 如果没有启用,那么`number`是可以为`null`的。 - diff --git a/zh/project-config/README.md b/zh/project-config/README.md index e4791e83..e9903534 100644 --- a/zh/project-config/README.md +++ b/zh/project-config/README.md @@ -1,10 +1,10 @@ # 工程配置 - * [tsconfig.json](project-config/tsconfig.json.md) - * [工程引用](project-config/project-references.md) - * [NPM包的类型](project-config/typings-for-npm-packages.md) - * [编译选项](project-config/compiler-options.md) - * [配置 Watch](project-config/configuring-watch.md) - * [在MSBuild里使用编译选项](project-config/compiler-options-in-msbuild.md) - * [与其它构建工具整合](project-config/integrating-with-build-tools.md) - * [使用TypeScript的每日构建版本](project-config/nightly-builds.md) \ No newline at end of file +- [tsconfig.json](project-config/tsconfig.json.md) +- [工程引用](project-config/project-references.md) +- [NPM 包的类型](project-config/typings-for-npm-packages.md) +- [编译选项](project-config/compiler-options.md) +- [配置 Watch](project-config/configuring-watch.md) +- [在 MSBuild 里使用编译选项](project-config/compiler-options-in-msbuild.md) +- [与其它构建工具整合](project-config/integrating-with-build-tools.md) +- [使用 TypeScript 的每日构建版本](project-config/nightly-builds.md) diff --git a/zh/project-config/compiler-options-in-msbuild.md b/zh/project-config/compiler-options-in-msbuild.md index d27b537d..7ad7914f 100644 --- a/zh/project-config/compiler-options-in-msbuild.md +++ b/zh/project-config/compiler-options-in-msbuild.md @@ -114,4 +114,3 @@ ## TypeScriptCompileBlocked 如果你使用其它的构建工具(比如,gulp, grunt等等)并且使用VS做为开发和调试工具,那么在工程里设置`true`。 这样VS只会提供给你编辑的功能,而不会在你按F5的时候去构建。 - diff --git a/zh/project-config/compiler-options.md b/zh/project-config/compiler-options.md index 7ce5cc3b..f7ffb5e7 100644 --- a/zh/project-config/compiler-options.md +++ b/zh/project-config/compiler-options.md @@ -94,4 +94,3 @@ * 在[`tsconfig.json`](tsconfig.json.md)文件里设置编译器选项。 * 在[MSBuild工程](compiler-options-in-msbuild.md)里设置编译器选项。 - diff --git a/zh/project-config/configuring-watch.md b/zh/project-config/configuring-watch.md index 2a5bd4e3..4797994a 100644 --- a/zh/project-config/configuring-watch.md +++ b/zh/project-config/configuring-watch.md @@ -30,4 +30,3 @@ `fs.watch`使用文件系统事件通知文件及目录的变化。 但是它依赖于操作系统,且事件通知并不完全可靠,在很多操作系统上的行为难以预料。 还可能会有创建监视个数的限制,如Linux系统,在包含大量文件的程序中监视器个数很快被耗尽。 但也正是因为它使用文件系统事件,不需要占用过多的CPU周期。 典型地,编译器使用`fs.watch`来监视目录(比如配置文件里声明的源码目录,无法进行模块解析的目录)。 这样就可以处理改动通知不准确的问题。 但递归地监视仅在Windows和OSX系统上支持。 这就意味着在其它系统上要使用替代方案。 `fs.watchFile`使用轮询,因此涉及到CPU周期。 但是这是最可靠的获取文件/目录状态的机制。 典型地,编译器使用`fs.watchFile`监视源文件,配置文件和消失的文件(失去文件引用),这意味着对CPU的使用依赖于程序里文件的数量。 - diff --git a/zh/project-config/integrating-with-build-tools.md b/zh/project-config/integrating-with-build-tools.md index 16ce1cfa..3e493fad 100644 --- a/zh/project-config/integrating-with-build-tools.md +++ b/zh/project-config/integrating-with-build-tools.md @@ -274,4 +274,3 @@ module.exports = { * 安装完成后,Rebuild。 更多详细信息请参考[Package Manager Dialog](http://docs.nuget.org/Consume/Package-Manager-Dialog)和[using nightly builds with NuGet](https://github.com/Microsoft/TypeScript/wiki/Nightly-drops#using-nuget-with-msbuild) - diff --git a/zh/project-config/project-references.md b/zh/project-config/project-references.md index c6ce0386..c44408b8 100644 --- a/zh/project-config/project-references.md +++ b/zh/project-config/project-references.md @@ -182,4 +182,3 @@ B C ### `outFile`的结构 使用了`outFile`的编译输出结构十分灵活,因为相对路径是无关紧要的。 要注意的是,你通常不需要使用`prepend` - 因为这会改善构建时间并结省I/O。 TypeScript项目本身是一个好的参照 - 我们有一些“library”的工程和一些“endpoint”工程,“endpoint”工程会确保足够小并仅仅导入它们需要的“library”。 - diff --git a/zh/project-config/tsconfig.json.md b/zh/project-config/tsconfig.json.md index e6ef0a94..e8329933 100644 --- a/zh/project-config/tsconfig.json.md +++ b/zh/project-config/tsconfig.json.md @@ -188,4 +188,3 @@ ## 模式 到这里查看模式: [http://json.schemastore.org/tsconfig](http://json.schemastore.org/tsconfig). - diff --git a/zh/reference/advanced-types.md b/zh/reference/advanced-types.md index 1d48f3fa..79ff227a 100644 --- a/zh/reference/advanced-types.md +++ b/zh/reference/advanced-types.md @@ -4,36 +4,36 @@ 交叉类型是将多个类型合并为一个类型。 这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。 例如,`Person & Serializable & Loggable`同时是`Person`_和_`Serializable`_和_`Loggable`。 就是说这个类型的对象同时拥有了这三种类型的成员。 -我们大多是在混入(mixins)或其它不适合典型面向对象模型的地方看到交叉类型的使用。 (在JavaScript里发生这种情况的场合很多!) 下面是如何创建混入的一个简单例子\("target": "es5"\): +我们大多是在混入(mixins)或其它不适合典型面向对象模型的地方看到交叉类型的使用。 (在 JavaScript 里发生这种情况的场合很多!) 下面是如何创建混入的一个简单例子\("target": "es5"\): ```typescript function extend(first: First, second: Second): First & Second { - const result: Partial = {}; - for (const prop in first) { - if (first.hasOwnProperty(prop)) { - (result as First)[prop] = first[prop]; - } + const result: Partial = {}; + for (const prop in first) { + if (first.hasOwnProperty(prop)) { + (result as First)[prop] = first[prop]; } - for (const prop in second) { - if (second.hasOwnProperty(prop)) { - (result as Second)[prop] = second[prop]; - } + } + for (const prop in second) { + if (second.hasOwnProperty(prop)) { + (result as Second)[prop] = second[prop]; } - return result as First & Second; + } + return result as First & Second; } class Person { - constructor(public name: string) { } + constructor(public name: string) {} } interface Loggable { - log(name: string): void; + log(name: string): void; } class ConsoleLogger implements Loggable { - log(name) { - console.log(`Hello, I'm ${name}.`); - } + log(name) { + console.log(`Hello, I'm ${name}.`); + } } const jim = extend(new Person('Jim'), ConsoleLogger.prototype); @@ -51,27 +51,27 @@ jim.log(jim.name); * If 'padding' is a number, then that number of spaces is added to the left side. */ function padLeft(value: string, padding: any) { - if (typeof padding === "number") { - return Array(padding + 1).join(" ") + value; - } - if (typeof padding === "string") { - return padding + value; - } - throw new Error(`Expected string or number, got '${padding}'.`); + if (typeof padding === 'number') { + return Array(padding + 1).join(' ') + value; + } + if (typeof padding === 'string') { + return padding + value; + } + throw new Error(`Expected string or number, got '${padding}'.`); } -padLeft("Hello world", 4); // returns " Hello world" +padLeft('Hello world', 4); // returns " Hello world" ``` -`padLeft`存在一个问题,`padding`参数的类型指定成了`any`。 这就是说我们可以传入一个既不是`number`也不是`string`类型的参数,但是TypeScript却不报错。 +`padLeft`存在一个问题,`padding`参数的类型指定成了`any`。 这就是说我们可以传入一个既不是`number`也不是`string`类型的参数,但是 TypeScript 却不报错。 ```typescript -let indentedString = padLeft("Hello world", true); // 编译阶段通过,运行时报错 +let indentedString = padLeft('Hello world', true); // 编译阶段通过,运行时报错 ``` 在传统的面向对象语言里,我们可能会将这两种类型抽象成有层级的类型。 这么做显然是非常清晰的,但同时也存在了过度设计。 `padLeft`原始版本的好处之一是允许我们传入原始类型。 这样做的话使用起来既简单又方便。 如果我们就是想使用已经存在的函数的话,这种新的方式就不适用了。 -代替`any`, 我们可以使用_联合类型_做为`padding`的参数: +代替`any`, 我们可以使用*联合类型*做为`padding`的参数: ```typescript /** @@ -80,10 +80,10 @@ let indentedString = padLeft("Hello world", true); // 编译阶段通过,运 * If 'padding' is a number, then that number of spaces is added to the left side. */ function padLeft(value: string, padding: string | number) { - // ... + // ... } -let indentedString = padLeft("Hello world", true); // errors during compilation +let indentedString = padLeft('Hello world', true); // errors during compilation ``` 联合类型表示一个值可以是几种类型之一。 我们用竖线(`|`)分隔每个类型,所以`number | string | boolean`表示一个值可以是`number`,`string`,或`boolean`。 @@ -92,39 +92,38 @@ let indentedString = padLeft("Hello world", true); // errors during compilation ```typescript interface Bird { - fly(); - layEggs(); + fly(); + layEggs(); } interface Fish { - swim(); - layEggs(); + swim(); + layEggs(); } function getSmallPet(): Fish | Bird { - // ... + // ... } let pet = getSmallPet(); pet.layEggs(); // okay -pet.swim(); // errors +pet.swim(); // errors ``` -这里的联合类型可能有点复杂,但是你很容易就习惯了。 如果一个值的类型是`A | B`,我们能够_确定_的是它包含了`A`_和_`B`中共有的成员。 这个例子里,`Bird`具有一个`fly`成员。 我们不能确定一个`Bird | Fish`类型的变量是否有`fly`方法。 如果变量在运行时是`Fish`类型,那么调用`pet.fly()`就出错了。 +这里的联合类型可能有点复杂,但是你很容易就习惯了。 如果一个值的类型是`A | B`,我们能够*确定*的是它包含了`A`_和_`B`中共有的成员。 这个例子里,`Bird`具有一个`fly`成员。 我们不能确定一个`Bird | Fish`类型的变量是否有`fly`方法。 如果变量在运行时是`Fish`类型,那么调用`pet.fly()`就出错了。 ## 类型守卫与类型区分(Type Guards and Differentiating Types) -联合类型适合于那些值可以为不同类型的情况。 但当我们想确切地了解是否为`Fish`时怎么办? JavaScript里常用来区分2个可能值的方法是检查成员是否存在。 如之前提及的,我们只能访问联合类型中共同拥有的成员。 +联合类型适合于那些值可以为不同类型的情况。 但当我们想确切地了解是否为`Fish`时怎么办? JavaScript 里常用来区分 2 个可能值的方法是检查成员是否存在。 如之前提及的,我们只能访问联合类型中共同拥有的成员。 ```typescript let pet = getSmallPet(); // 每一个成员访问都会报错 if (pet.swim) { - pet.swim(); -} -else if (pet.fly) { - pet.fly(); + pet.swim(); +} else if (pet.fly) { + pet.fly(); } ``` @@ -134,9 +133,9 @@ else if (pet.fly) { let pet = getSmallPet(); if ((pet as Fish).swim) { - (pet as Fish).swim(); + (pet as Fish).swim(); } else if ((pet as Bird).fly) { - (pet as Bird).fly(); + (pet as Bird).fly(); } ``` @@ -144,34 +143,33 @@ if ((pet as Fish).swim) { 这里可以注意到我们不得不多次使用类型断言。 假若我们一旦检查过类型,就能在之后的每个分支里清楚地知道`pet`的类型的话就好了。 -TypeScript里的_类型守卫_机制让它成为了现实。 类型守卫就是一些表达式,它们会在运行时检查以确保在某个作用域里的类型。 +TypeScript 里的*类型守卫*机制让它成为了现实。 类型守卫就是一些表达式,它们会在运行时检查以确保在某个作用域里的类型。 #### 使用类型判定 -要定义一个类型守卫,我们只要简单地定义一个函数,它的返回值是一个_类型谓词_: +要定义一个类型守卫,我们只要简单地定义一个函数,它的返回值是一个*类型谓词*: ```typescript function isFish(pet: Fish | Bird): pet is Fish { - return (pet as Fish).swim !== undefined; + return (pet as Fish).swim !== undefined; } ``` 在这个例子里,`pet is Fish`就是类型谓词。 谓词为`parameterName is Type`这种形式,`parameterName`必须是来自于当前函数签名里的一个参数名。 -每当使用一些变量调用`isFish`时,TypeScript会将变量缩减为那个具体的类型,只要这个类型与变量的原始类型是兼容的。 +每当使用一些变量调用`isFish`时,TypeScript 会将变量缩减为那个具体的类型,只要这个类型与变量的原始类型是兼容的。 ```typescript // 'swim' 和 'fly' 调用都没有问题了 if (isFish(pet)) { - pet.swim(); -} -else { - pet.fly(); + pet.swim(); +} else { + pet.fly(); } ``` -注意TypeScript不仅知道在`if`分支里`pet`是`Fish`类型; 它还清楚在`else`分支里,一定_不是_`Fish`类型,一定是`Bird`类型。 +注意 TypeScript 不仅知道在`if`分支里`pet`是`Fish`类型; 它还清楚在`else`分支里,一定*不是*`Fish`类型,一定是`Bird`类型。 #### 使用`in`操作符 @@ -181,10 +179,10 @@ else { ```typescript function move(pet: Fish | Bird) { - if ("swim" in pet) { - return pet.swim(); - } - return pet.fly(); + if ('swim' in pet) { + return pet.swim(); + } + return pet.fly(); } ``` @@ -194,83 +192,83 @@ function move(pet: Fish | Bird) { ```typescript function isNumber(x: any): x is number { - return typeof x === "number"; + return typeof x === 'number'; } function isString(x: any): x is string { - return typeof x === "string"; + return typeof x === 'string'; } function padLeft(value: string, padding: string | number) { - if (isNumber(padding)) { - return Array(padding + 1).join(" ") + value; - } - if (isString(padding)) { - return padding + value; - } - throw new Error(`Expected string or number, got '${padding}'.`); + if (isNumber(padding)) { + return Array(padding + 1).join(' ') + value; + } + if (isString(padding)) { + return padding + value; + } + throw new Error(`Expected string or number, got '${padding}'.`); } ``` -然而,必须要定义一个函数来判断类型是否是原始类型,这太痛苦了。 幸运的是,现在我们不必将`typeof x === "number"`抽象成一个函数,因为TypeScript可以将它识别为一个类型守卫。 也就是说我们可以直接在代码里检查类型了。 +然而,必须要定义一个函数来判断类型是否是原始类型,这太痛苦了。 幸运的是,现在我们不必将`typeof x === "number"`抽象成一个函数,因为 TypeScript 可以将它识别为一个类型守卫。 也就是说我们可以直接在代码里检查类型了。 ```typescript function padLeft(value: string, padding: string | number) { - if (typeof padding === "number") { - return Array(padding + 1).join(" ") + value; - } - if (typeof padding === "string") { - return padding + value; - } - throw new Error(`Expected string or number, got '${padding}'.`); + if (typeof padding === 'number') { + return Array(padding + 1).join(' ') + value; + } + if (typeof padding === 'string') { + return padding + value; + } + throw new Error(`Expected string or number, got '${padding}'.`); } ``` -这些_`typeof`类型守卫_只有两种形式能被识别:`typeof v === "typename"`和`typeof v !== "typename"`,`"typename"`必须是`"number"`,`"string"`,`"boolean"`或`"symbol"`。 但是TypeScript并不会阻止你与其它字符串比较,语言不会把那些表达式识别为类型守卫。 +这些*`typeof`类型守卫*只有两种形式能被识别:`typeof v === "typename"`和`typeof v !== "typename"`,`"typename"`必须是`"number"`,`"string"`,`"boolean"`或`"symbol"`。 但是 TypeScript 并不会阻止你与其它字符串比较,语言不会把那些表达式识别为类型守卫。 ### `instanceof`类型守卫 -如果你已经阅读了`typeof`类型守卫并且对JavaScript里的`instanceof`操作符熟悉的话,你可能已经猜到了这节要讲的内容。 +如果你已经阅读了`typeof`类型守卫并且对 JavaScript 里的`instanceof`操作符熟悉的话,你可能已经猜到了这节要讲的内容。 -_`instanceof`类型守卫_是通过构造函数来细化类型的一种方式。 比如,我们借鉴一下之前字符串填充的例子: +*`instanceof`类型守卫*是通过构造函数来细化类型的一种方式。 比如,我们借鉴一下之前字符串填充的例子: ```typescript interface Padder { - getPaddingString(): string + getPaddingString(): string; } class SpaceRepeatingPadder implements Padder { - constructor(private numSpaces: number) { } - getPaddingString() { - return Array(this.numSpaces + 1).join(" "); - } + constructor(private numSpaces: number) {} + getPaddingString() { + return Array(this.numSpaces + 1).join(' '); + } } class StringPadder implements Padder { - constructor(private value: string) { } - getPaddingString() { - return this.value; - } + constructor(private value: string) {} + getPaddingString() { + return this.value; + } } function getRandomPadder() { - return Math.random() < 0.5 ? - new SpaceRepeatingPadder(4) : - new StringPadder(" "); + return Math.random() < 0.5 + ? new SpaceRepeatingPadder(4) + : new StringPadder(' '); } // 类型为SpaceRepeatingPadder | StringPadder let padder: Padder = getRandomPadder(); if (padder instanceof SpaceRepeatingPadder) { - padder; // 类型细化为'SpaceRepeatingPadder' + padder; // 类型细化为'SpaceRepeatingPadder' } if (padder instanceof StringPadder) { - padder; // 类型细化为'StringPadder' + padder; // 类型细化为'StringPadder' } ``` -`instanceof`的右侧要求是一个构造函数,TypeScript将细化为: +`instanceof`的右侧要求是一个构造函数,TypeScript 将细化为: 1. 此构造函数的`prototype`属性的类型,如果它的类型不为`any`的话 2. 构造签名所返回的类型的联合 @@ -279,20 +277,20 @@ if (padder instanceof StringPadder) { ## 可以为`null`的类型 -TypeScript具有两种特殊的类型,`null`和`undefined`,它们分别具有值`null`和`undefined`. 我们在[基础类型](basic-types.md)一节里已经做过简要说明。 默认情况下,类型检查器认为`null`与`undefined`可以赋值给任何类型。 `null`与`undefined`是所有其它类型的一个有效值。 这也意味着,你阻止不了将它们赋值给其它类型,就算是你想要阻止这种情况也不行。 `null`的发明者,Tony Hoare,称它为[价值亿万美金的错误](https://en.wikipedia.org/wiki/Null_pointer#History)。 +TypeScript 具有两种特殊的类型,`null`和`undefined`,它们分别具有值`null`和`undefined`. 我们在[基础类型](basic-types.md)一节里已经做过简要说明。 默认情况下,类型检查器认为`null`与`undefined`可以赋值给任何类型。 `null`与`undefined`是所有其它类型的一个有效值。 这也意味着,你阻止不了将它们赋值给其它类型,就算是你想要阻止这种情况也不行。 `null`的发明者,Tony Hoare,称它为[价值亿万美金的错误](https://en.wikipedia.org/wiki/Null_pointer#History)。 `--strictNullChecks`标记可以解决此错误:当你声明一个变量时,它不会自动地包含`null`或`undefined`。 你可以使用联合类型明确的包含它们: ```typescript -let s = "foo"; +let s = 'foo'; s = null; // 错误, 'null'不能赋值给'string' -let sn: string | null = "bar"; +let sn: string | null = 'bar'; sn = null; // 可以 sn = undefined; // error, 'undefined'不能赋值给'string | null' ``` -注意,按照JavaScript的语义,TypeScript会把`null`和`undefined`区别对待。 `string | null`,`string | undefined`和`string | undefined | null`是不同的类型。 +注意,按照 JavaScript 的语义,TypeScript 会把`null`和`undefined`区别对待。 `string | null`,`string | undefined`和`string | undefined | null`是不同的类型。 ### 可选参数和可选属性 @@ -300,7 +298,7 @@ sn = undefined; // error, 'undefined'不能赋值给'string | null' ```typescript function f(x: number, y?: number) { - return x + (y || 0); + return x + (y || 0); } f(1, 2); f(1); @@ -312,8 +310,8 @@ f(1, null); // error, 'null' is not assignable to 'number | undefined' ```typescript class C { - a: number; - b?: number; + a: number; + b?: number; } let c = new C(); c.a = 12; @@ -325,16 +323,15 @@ c.b = null; // error, 'null' is not assignable to 'number | undefined' ### 类型守卫和类型断言 -由于可以为`null`的类型是通过联合类型实现,那么你需要使用类型守卫来去除`null`。 幸运地是这与在JavaScript里写的代码一致: +由于可以为`null`的类型是通过联合类型实现,那么你需要使用类型守卫来去除`null`。 幸运地是这与在 JavaScript 里写的代码一致: ```typescript function f(sn: string | null): string { - if (sn == null) { - return "default"; - } - else { - return sn; - } + if (sn == null) { + return 'default'; + } else { + return sn; + } } ``` @@ -342,7 +339,7 @@ function f(sn: string | null): string { ```typescript function f(sn: string | null): string { - return sn || "default"; + return sn || 'default'; } ``` @@ -353,16 +350,16 @@ function broken(name: string | null): string { function postfix(epithet: string) { return name.charAt(0) + '. the ' + epithet; // error, 'name' is possibly null } - name = name || "Bob"; - return postfix("great"); + name = name || 'Bob'; + return postfix('great'); } function fixed(name: string | null): string { function postfix(epithet: string) { return name!.charAt(0) + '. the ' + epithet; // ok } - name = name || "Bob"; - return postfix("great"); + name = name || 'Bob'; + return postfix('great'); } ``` @@ -377,16 +374,15 @@ type Name = string; type NameResolver = () => string; type NameOrResolver = Name | NameResolver; function getName(n: NameOrResolver): Name { - if (typeof n === 'string') { - return n; - } - else { - return n(); - } + if (typeof n === 'string') { + return n; + } else { + return n(); + } } ``` -起别名不会新建一个类型 - 它创建了一个新_名字_来引用那个类型。 给原始类型起别名通常没什么用,尽管可以做为文档的一种形式使用。 +起别名不会新建一个类型 - 它创建了一个新*名字*来引用那个类型。 给原始类型起别名通常没什么用,尽管可以做为文档的一种形式使用。 同接口一样,类型别名也可以是泛型 - 我们可以添加类型参数并且在别名声明的右侧传入: @@ -398,10 +394,10 @@ type Container = { value: T }; ```typescript type Tree = { - value: T; - left: Tree; - right: Tree; -} + value: T; + left: Tree; + right: Tree; +}; ``` 与交叉类型一起使用,我们可以创建出一些十分稀奇古怪的类型。 @@ -410,7 +406,7 @@ type Tree = { type LinkedList = T & { next: LinkedList }; interface Person { - name: string; + name: string; } var people: LinkedList; @@ -433,15 +429,15 @@ type Yikes = Array; // error 其一,接口创建了一个新的名字,可以在其它任何地方使用。 类型别名并不创建新名字—比如,错误信息就不会使用别名。 在下面的示例代码里,在编译器中将鼠标悬停在`interfaced`上,显示它返回的是`Interface`,但悬停在`aliased`上时,显示的却是对象字面量类型。 ```typescript -type Alias = { num: number } +type Alias = { num: number }; interface Interface { - num: number; + num: number; } declare function aliased(arg: Alias): Alias; declare function interfaced(arg: Interface): Interface; ``` -在旧版本的TypeScript里,类型别名不能被继承和实现(它们也不能继承和实现其它类型)。从TypeScript 2.7开始,类型别名可以被继承并生成新的交叉类型。例如:`type Cat = Animal & { purrs: true }`。 +在旧版本的 TypeScript 里,类型别名不能被继承和实现(它们也不能继承和实现其它类型)。从 TypeScript 2.7 开始,类型别名可以被继承并生成新的交叉类型。例如:`type Cat = Animal & { purrs: true }`。 因为[软件中的对象应该对于扩展是开放的,但是对于修改是封闭的](https://en.wikipedia.org/wiki/Open/closed_principle),你应该尽量去使用接口代替类型别名。 @@ -452,25 +448,22 @@ declare function interfaced(arg: Interface): Interface; 字符串字面量类型允许你指定字符串必须的固定值。 在实际应用中,字符串字面量类型可以与联合类型,类型守卫和类型别名很好的配合。 通过结合使用这些特性,你可以实现类似枚举类型的字符串。 ```typescript -type Easing = "ease-in" | "ease-out" | "ease-in-out"; +type Easing = 'ease-in' | 'ease-out' | 'ease-in-out'; class UIElement { - animate(dx: number, dy: number, easing: Easing) { - if (easing === "ease-in") { - // ... - } - else if (easing === "ease-out") { - } - else if (easing === "ease-in-out") { - } - else { - // error! should not pass null or undefined. - } + animate(dx: number, dy: number, easing: Easing) { + if (easing === 'ease-in') { + // ... + } else if (easing === 'ease-out') { + } else if (easing === 'ease-in-out') { + } else { + // error! should not pass null or undefined. } + } } let button = new UIElement(); -button.animate(0, 0, "ease-in"); -button.animate(0, 0, "uneasy"); // error: "uneasy" is not allowed here +button.animate(0, 0, 'ease-in'); +button.animate(0, 0, 'uneasy'); // error: "uneasy" is not allowed here ``` 你只能从三种允许的字符中选择其一来做为参数传递,传入其它值则会产生错误。 @@ -482,32 +475,32 @@ Argument of type '"uneasy"' is not assignable to parameter of type '"ease-in" | 字符串字面量类型还可以用于区分函数重载: ```typescript -function createElement(tagName: "img"): HTMLImageElement; -function createElement(tagName: "input"): HTMLInputElement; +function createElement(tagName: 'img'): HTMLImageElement; +function createElement(tagName: 'input'): HTMLInputElement; // ... more overloads ... function createElement(tagName: string): Element { - // ... code goes here ... + // ... code goes here ... } ``` ## 数字字面量类型 -TypeScript还具有数字字面量类型。 +TypeScript 还具有数字字面量类型。 ```typescript function rollDice(): 1 | 2 | 3 | 4 | 5 | 6 { - // ... + // ... } ``` -我们很少直接这样使用,但它们可以用在缩小范围调试bug的时候: +我们很少直接这样使用,但它们可以用在缩小范围调试 bug 的时候: ```typescript function foo(x: number) { - if (x !== 1 || x !== 2) { - // ~~~~~~~ - // Operator '!==' cannot be applied to types '1' and '2'. - } + if (x !== 1 || x !== 2) { + // ~~~~~~~ + // Operator '!==' cannot be applied to types '1' and '2'. + } } ``` @@ -521,7 +514,7 @@ function foo(x: number) { ## 可辨识联合(Discriminated Unions) -你可以合并单例类型,联合类型,类型守卫和类型别名来创建一个叫做_可辨识联合_的高级模式,它也称做_标签联合_或_代数数据类型_。 可辨识联合在函数式编程里很有用处。 一些语言会自动地为你辨识联合;而TypeScript则基于已有的JavaScript模式。 它具有3个要素: +你可以合并单例类型,联合类型,类型守卫和类型别名来创建一个叫做*可辨识联合*的高级模式,它也称做*标签联合*或*代数数据类型*。 可辨识联合在函数式编程里很有用处。 一些语言会自动地为你辨识联合;而 TypeScript 则基于已有的 JavaScript 模式。 它具有 3 个要素: 1. 具有普通的单例类型属性—_可辨识的特征_。 2. 一个类型别名包含了那些类型的联合—_联合_。 @@ -529,21 +522,21 @@ function foo(x: number) { ```typescript interface Square { - kind: "square"; - size: number; + kind: 'square'; + size: number; } interface Rectangle { - kind: "rectangle"; - width: number; - height: number; + kind: 'rectangle'; + width: number; + height: number; } interface Circle { - kind: "circle"; - radius: number; + kind: 'circle'; + radius: number; } ``` -首先我们声明了将要联合的接口。 每个接口都有`kind`属性但有不同的字符串字面量类型。 `kind`属性称做_可辨识的特征_或_标签_。 其它的属性则特定于各个接口。 注意,目前各个接口间是没有联系的。 下面我们把它们联合到一起: +首先我们声明了将要联合的接口。 每个接口都有`kind`属性但有不同的字符串字面量类型。 `kind`属性称做*可辨识的特征*或*标签*。 其它的属性则特定于各个接口。 注意,目前各个接口间是没有联系的。 下面我们把它们联合到一起: ```typescript type Shape = Square | Rectangle | Circle; @@ -553,11 +546,14 @@ type Shape = Square | Rectangle | Circle; ```typescript function area(s: Shape) { - switch (s.kind) { - case "square": return s.size * s.size; - case "rectangle": return s.height * s.width; - case "circle": return Math.PI * s.radius ** 2; - } + switch (s.kind) { + case 'square': + return s.size * s.size; + case 'rectangle': + return s.height * s.width; + case 'circle': + return Math.PI * s.radius ** 2; + } } ``` @@ -568,108 +564,112 @@ function area(s: Shape) { ```typescript type Shape = Square | Rectangle | Circle | Triangle; function area(s: Shape) { - switch (s.kind) { - case "square": return s.size * s.size; - case "rectangle": return s.height * s.width; - case "circle": return Math.PI * s.radius ** 2; - } - // should error here - we didn't handle case "triangle" + switch (s.kind) { + case 'square': + return s.size * s.size; + case 'rectangle': + return s.height * s.width; + case 'circle': + return Math.PI * s.radius ** 2; + } + // should error here - we didn't handle case "triangle" } ``` 有两种方式可以实现。 首先是启用`--strictNullChecks`并且指定一个返回值类型: ```typescript -function area(s: Shape): number { // error: returns number | undefined - switch (s.kind) { - case "square": return s.size * s.size; - case "rectangle": return s.height * s.width; - case "circle": return Math.PI * s.radius ** 2; - } +function area(s: Shape): number { + // error: returns number | undefined + switch (s.kind) { + case 'square': + return s.size * s.size; + case 'rectangle': + return s.height * s.width; + case 'circle': + return Math.PI * s.radius ** 2; + } } ``` -因为`switch`没有包含所有情况,所以TypeScript认为这个函数有时候会返回`undefined`。 如果你明确地指定了返回值类型为`number`,那么你会看到一个错误,因为实际上返回值的类型为`number | undefined`。 然而,这种方法存在些微妙之处且`--strictNullChecks`对旧代码支持不好。 +因为`switch`没有包含所有情况,所以 TypeScript 认为这个函数有时候会返回`undefined`。 如果你明确地指定了返回值类型为`number`,那么你会看到一个错误,因为实际上返回值的类型为`number | undefined`。 然而,这种方法存在些微妙之处且`--strictNullChecks`对旧代码支持不好。 第二种方法使用`never`类型,编译器用它来进行完整性检查: ```typescript function assertNever(x: never): never { - throw new Error("Unexpected object: " + x); + throw new Error('Unexpected object: ' + x); } function area(s: Shape) { - switch (s.kind) { - case "square": return s.size * s.size; - case "rectangle": return s.height * s.width; - case "circle": return Math.PI * s.radius ** 2; - default: return assertNever(s); // error here if there are missing cases - } + switch (s.kind) { + case 'square': + return s.size * s.size; + case 'rectangle': + return s.height * s.width; + case 'circle': + return Math.PI * s.radius ** 2; + default: + return assertNever(s); // error here if there are missing cases + } } ``` -这里,`assertNever`检查`s`是否为`never`类型—即为除去所有可能情况后剩下的类型。 如果你忘记了某个case,那么`s`将具有一个真实的类型并且你会得到一个错误。 这种方式需要你定义一个额外的函数,但是在你忘记某个case的时候也更加明显。 +这里,`assertNever`检查`s`是否为`never`类型—即为除去所有可能情况后剩下的类型。 如果你忘记了某个 case,那么`s`将具有一个真实的类型并且你会得到一个错误。 这种方式需要你定义一个额外的函数,但是在你忘记某个 case 的时候也更加明显。 ## 多态的`this`类型 -多态的`this`类型表示的是某个包含类或接口的_子类型_。 这被称做_F_-bounded多态性。 它能很容易的表现连贯接口间的继承,比如。 在计算器的例子里,在每个操作之后都返回`this`类型: +多态的`this`类型表示的是某个包含类或接口的*子类型*。 这被称做*F*-bounded 多态性。 它能很容易的表现连贯接口间的继承,比如。 在计算器的例子里,在每个操作之后都返回`this`类型: ```typescript class BasicCalculator { - public constructor(protected value: number = 0) { } - public currentValue(): number { - return this.value; - } - public add(operand: number): this { - this.value += operand; - return this; - } - public multiply(operand: number): this { - this.value *= operand; - return this; - } - // ... other operations go here ... + public constructor(protected value: number = 0) {} + public currentValue(): number { + return this.value; + } + public add(operand: number): this { + this.value += operand; + return this; + } + public multiply(operand: number): this { + this.value *= operand; + return this; + } + // ... other operations go here ... } -let v = new BasicCalculator(2) - .multiply(5) - .add(1) - .currentValue(); +let v = new BasicCalculator(2).multiply(5).add(1).currentValue(); ``` 由于这个类使用了`this`类型,你可以继承它,新的类可以直接使用之前的方法,不需要做任何的改变。 ```typescript class ScientificCalculator extends BasicCalculator { - public constructor(value = 0) { - super(value); - } - public sin() { - this.value = Math.sin(this.value); - return this; - } - // ... other operations go here ... + public constructor(value = 0) { + super(value); + } + public sin() { + this.value = Math.sin(this.value); + return this; + } + // ... other operations go here ... } -let v = new ScientificCalculator(2) - .multiply(5) - .sin() - .add(1) - .currentValue(); +let v = new ScientificCalculator(2).multiply(5).sin().add(1).currentValue(); ``` 如果没有`this`类型,`ScientificCalculator`就不能够在继承`BasicCalculator`的同时还保持接口的连贯性。 `multiply`将会返回`BasicCalculator`,它并没有`sin`方法。 然而,使用`this`类型,`multiply`会返回`this`,在这里就是`ScientificCalculator`。 ## 索引类型(Index types) -使用索引类型,编译器就能够检查使用了动态属性名的代码。 例如,一个常见的JavaScript模式是从对象中选取属性的子集。 +使用索引类型,编译器就能够检查使用了动态属性名的代码。 例如,一个常见的 JavaScript 模式是从对象中选取属性的子集。 ```javascript function pluck(o, propertyNames) { - return propertyNames.map(n => o[n]); + return propertyNames.map(n => o[n]); } ``` -下面是如何在TypeScript里使用此函数,通过**索引类型查询**和**索引访问**操作符: +下面是如何在 TypeScript 里使用此函数,通过**索引类型查询**和**索引访问**操作符: ```typescript function pluck(o: T, propertyNames: K[]): T[K][] { @@ -677,14 +677,14 @@ function pluck(o: T, propertyNames: K[]): T[K][] { } interface Car { - manufacturer: string; - model: string; - year: number; + manufacturer: string; + model: string; + year: number; } let taxi: Car = { - manufacturer: 'Toyota', - model: 'Camry', - year: 2014 + manufacturer: 'Toyota', + model: 'Camry', + year: 2014, }; // Manufacturer and model are both of type string, @@ -693,7 +693,7 @@ let makeAndModel: string[] = pluck(taxi, ['manufacturer', 'model']); // If we try to pluck model and year, we get an // array of a union type: (string | number)[] -let modelYear = pluck(taxi, ['model', 'year']) +let modelYear = pluck(taxi, ['model', 'year']); ``` 编译器会检查`manufacturer`和`model`是否真的是`Car`上的一个属性。 本例还引入了几个新的类型操作符。 首先是`keyof T`,**索引类型查询操作符**。 对于任何类型`T`,`keyof T`的结果为`T`上已知的公共属性名的联合。 例如: @@ -713,7 +713,7 @@ pluck(taxi, ['year', 'unknown']); ```typescript function getProperty(o: T, propertyName: K): T[K] { - return o[propertyName]; // o[propertyName] is of type T[K] + return o[propertyName]; // o[propertyName] is of type T[K] } ``` @@ -729,11 +729,11 @@ let unknown = getProperty(taxi, 'unknown'); ### 索引类型和字符串索引签名 -`keyof`和`T[K]`与字符串索引签名进行交互。索引签名的参数类型必须为`number`或`string`。 如果你有一个带有字符串索引签名的类型,那么`keyof T`会是`string | number`。 \(并非只有`string`,因为在JavaScript里,你可以使用字符串`object['42']`或 数字`object[42]`索引来访问对象属性\)。 并且`T[string]`为索引签名的类型: +`keyof`和`T[K]`与字符串索引签名进行交互。索引签名的参数类型必须为`number`或`string`。 如果你有一个带有字符串索引签名的类型,那么`keyof T`会是`string | number`。 \(并非只有`string`,因为在 JavaScript 里,你可以使用字符串`object['42']`或 数字`object[42]`索引来访问对象属性\)。 并且`T[string]`为索引签名的类型: ```typescript interface Dictionary { - [key: string]: T; + [key: string]: T; } let keys: keyof Dictionary; // string | number let value: Dictionary['foo']; // number @@ -743,7 +743,7 @@ let value: Dictionary['foo']; // number ```typescript interface Dictionary { - [key: number]: T; + [key: number]: T; } let keys: keyof Dictionary; // number let value: Dictionary['foo']; // Error, Property 'foo' does not exist on type 'Dictionary'. @@ -756,8 +756,8 @@ let value: Dictionary[42]; // number ```typescript interface PersonPartial { - name?: string; - age?: number; + name?: string; + age?: number; } ``` @@ -765,20 +765,20 @@ interface PersonPartial { ```typescript interface PersonReadonly { - readonly name: string; - readonly age: number; + readonly name: string; + readonly age: number; } ``` -这在JavaScript里经常出现,TypeScript提供了从旧类型中创建新类型的一种方式 — **映射类型**。 在映射类型里,新类型以相同的形式去转换旧类型里每个属性。 例如,你可以令每个属性成为`readonly`类型或可选的。 下面是一些例子: +这在 JavaScript 里经常出现,TypeScript 提供了从旧类型中创建新类型的一种方式 — **映射类型**。 在映射类型里,新类型以相同的形式去转换旧类型里每个属性。 例如,你可以令每个属性成为`readonly`类型或可选的。 下面是一些例子: ```typescript type Readonly = { - readonly [P in keyof T]: T[P]; -} + readonly [P in keyof T]: T[P]; +}; type Partial = { - [P in keyof T]?: T[P]; -} + [P in keyof T]?: T[P]; +}; ``` 像下面这样使用: @@ -794,13 +794,12 @@ type ReadonlyPerson = Readonly; // 这样使用 type PartialWithNewMember = { [P in keyof T]?: T[P]; -} & { newMember: boolean } +} & { newMember: boolean }; // 不要这样使用 // 这会报错! type PartialWithNewMember = { [P in keyof T]?: T[P]; - newMember: boolean; -} +}; ``` 下面来看看最简单的映射类型和它的组成部分: @@ -820,23 +819,23 @@ type Flags = { [K in Keys]: boolean }; ```typescript type Flags = { - option1: boolean; - option2: boolean; -} + option1: boolean; + option2: boolean; +}; ``` 在真正的应用里,可能不同于上面的`Readonly`或`Partial`。 它们会基于一些已存在的类型,且按照一定的方式转换字段。 这就是`keyof`和索引访问类型要做的事情: ```typescript -type NullablePerson = { [P in keyof Person]: Person[P] | null } -type PartialPerson = { [P in keyof Person]?: Person[P] } +type NullablePerson = { [P in keyof Person]: Person[P] | null }; +type PartialPerson = { [P in keyof Person]?: Person[P] }; ``` 但它更有用的地方是可以有一些通用版本。 ```typescript -type Nullable = { [P in keyof T]: T[P] | null } -type Partial = { [P in keyof T]?: T[P] } +type Nullable = { [P in keyof T]: T[P] | null }; +type Partial = { [P in keyof T]?: T[P] }; ``` 在这些例子里,属性列表是`keyof T`且结果类型是`T[P]`的变体。 这是使用通用映射类型的一个好模版。 因为这类转换是[同态](https://en.wikipedia.org/wiki/Homomorphism)的,映射只作用于`T`的属性而没有其它的。 编译器知道在添加任何新属性之前可以拷贝所有存在的属性修饰符。 例如,假设`Person.name`是只读的,那么`Partial.name`也将是只读的且为可选的。 @@ -845,33 +844,33 @@ type Partial = { [P in keyof T]?: T[P] } ```typescript type Proxy = { - get(): T; - set(value: T): void; -} + get(): T; + set(value: T): void; +}; type Proxify = { - [P in keyof T]: Proxy; -} + [P in keyof T]: Proxy; +}; function proxify(o: T): Proxify { - // ... wrap proxies ... + // ... wrap proxies ... } let proxyProps = proxify(props); ``` -注意`Readonly`和`Partial`用处不小,因此它们与`Pick`和`Record`一同被包含进了TypeScript的标准库里: +注意`Readonly`和`Partial`用处不小,因此它们与`Pick`和`Record`一同被包含进了 TypeScript 的标准库里: ```typescript type Pick = { - [P in K]: T[P]; -} + [P in K]: T[P]; +}; type Record = { - [P in K]: T; -} + [P in K]: T; +}; ``` `Readonly`,`Partial`和`Pick`是同态的,但`Record`不是。 因为`Record`并不需要输入类型来拷贝属性,所以它不属于同态: ```typescript -type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string> +type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>; ``` 非同态类型本质上会创建新的属性,因此它们不会从它处拷贝属性修饰符。 @@ -882,11 +881,11 @@ type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string> ```typescript function unproxify(t: Proxify): T { - let result = {} as T; - for (const k in t) { - result[k] = t[k].get(); - } - return result; + let result = {} as T; + for (const k in t) { + result[k] = t[k].get(); + } + return result; } let originalProps = unproxify(proxyProps); @@ -896,7 +895,7 @@ let originalProps = unproxify(proxyProps); ### 有条件类型 -TypeScript 2.8引入了_有条件类型_,它能够表示非统一的类型。 有条件的类型会以一个条件表达式进行类型关系检测,从而在两种类型中选择其一: +TypeScript 2.8 引入了*有条件类型*,它能够表示非统一的类型。 有条件的类型会以一个条件表达式进行类型关系检测,从而在两种类型中选择其一: ```typescript T extends U ? X : Y @@ -904,7 +903,7 @@ T extends U ? X : Y 上面的类型意思是,若`T`能够赋值给`U`,那么类型是`X`,否则为`Y`。 -有条件的类型`T extends U ? X : Y`或者_解析_为`X`,或者_解析_为`Y`,再或者_延迟_解析,因为它可能依赖一个或多个类型变量。 若`T`或`U`包含类型参数,那么是否解析为`X`或`Y`或推迟,取决于类型系统是否有足够的信息来确定`T`总是可以赋值给`U`。 +有条件的类型`T extends U ? X : Y`或者*解析*为`X`,或者*解析*为`Y`,再或者*延迟*解析,因为它可能依赖一个或多个类型变量。 若`T`或`U`包含类型参数,那么是否解析为`X`或`Y`或推迟,取决于类型系统是否有足够的信息来确定`T`总是可以赋值给`U`。 下面是一些类型可以被立即解析的例子: @@ -912,47 +911,51 @@ T extends U ? X : Y declare function f(x: T): T extends true ? string : number; // Type is 'string | number -let x = f(Math.random() < 0.5) +let x = f(Math.random() < 0.5); ``` 另外一个例子涉及`TypeName`类型别名,它使用了嵌套了有条件类型: ```typescript -type TypeName = - T extends string ? "string" : - T extends number ? "number" : - T extends boolean ? "boolean" : - T extends undefined ? "undefined" : - T extends Function ? "function" : - "object"; +type TypeName = T extends string + ? 'string' + : T extends number + ? 'number' + : T extends boolean + ? 'boolean' + : T extends undefined + ? 'undefined' + : T extends Function + ? 'function' + : 'object'; -type T0 = TypeName; // "string" -type T1 = TypeName<"a">; // "string" -type T2 = TypeName; // "boolean" -type T3 = TypeName<() => void>; // "function" -type T4 = TypeName; // "object" +type T0 = TypeName; // "string" +type T1 = TypeName<'a'>; // "string" +type T2 = TypeName; // "boolean" +type T3 = TypeName<() => void>; // "function" +type T4 = TypeName; // "object" ``` 下面是一个有条件类型被推迟解析的例子: ```typescript interface Foo { - propA: boolean; - propB: boolean; + propA: boolean; + propB: boolean; } declare function f(x: T): T extends Foo ? string : number; function foo(x: U) { - // Has type 'U extends Foo ? string : number' - let a = f(x); + // Has type 'U extends Foo ? string : number' + let a = f(x); - // This assignment is allowed though! - let b: string | number = a; + // This assignment is allowed though! + let b: string | number = a; } ``` -这里,`a`变量含有未确定的有条件类型。 当有另一段代码调用`foo`,它会用其它类型替换`U`,TypeScript将重新计算有条件类型,决定它是否可以选择一个分支。 +这里,`a`变量含有未确定的有条件类型。 当有另一段代码调用`foo`,它会用其它类型替换`U`,TypeScript 将重新计算有条件类型,决定它是否可以选择一个分支。 与此同时,我们可以将有条件类型赋值给其它类型,只要有条件类型的每个分支都可以赋值给目标类型。 因此在我们的例子里,我们可以将`U extends Foo ? string : number`赋值给`string | number`,因为不管这个有条件类型最终结果是什么,它只能是`string`或`number`。 @@ -963,9 +966,9 @@ function foo(x: U) { **例子** ```typescript -type T10 = TypeName void)>; // "string" | "function" -type T12 = TypeName; // "string" | "object" | "undefined" -type T11 = TypeName; // "object" +type T10 = TypeName void)>; // "string" | "function" +type T12 = TypeName; // "string" | "object" | "undefined" +type T11 = TypeName; // "object" ``` 在`T extends U ? X : Y`的实例化里,对`T`的引用被解析为联合类型的一部分(比如,`T`指向某一单个部分,在有条件类型分布到联合类型之后)。 此外,在`X`内对`T`的引用有一个附加的类型参数约束`U`(例如,`T`被当成在`X`内可赋值给`U`)。 @@ -977,62 +980,66 @@ type BoxedValue = { value: T }; type BoxedArray = { array: T[] }; type Boxed = T extends any[] ? BoxedArray : BoxedValue; -type T20 = Boxed; // BoxedValue; -type T21 = Boxed; // BoxedArray; -type T22 = Boxed; // BoxedValue | BoxedArray; +type T20 = Boxed; // BoxedValue; +type T21 = Boxed; // BoxedArray; +type T22 = Boxed; // BoxedValue | BoxedArray; ``` 注意在`Boxed`的`true`分支里,`T`有个额外的约束`any[]`,因此它适用于`T[number]`数组元素类型。同时也注意一下有条件类型是如何分布成联合类型的。 -有条件类型的分布式的属性可以方便地用来_过滤_联合类型: +有条件类型的分布式的属性可以方便地用来*过滤*联合类型: ```typescript -type Diff = T extends U ? never : T; // Remove types from T that are assignable to U -type Filter = T extends U ? T : never; // Remove types from T that are not assignable to U +type Diff = T extends U ? never : T; // Remove types from T that are assignable to U +type Filter = T extends U ? T : never; // Remove types from T that are not assignable to U -type T30 = Diff<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "b" | "d" -type T31 = Filter<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "a" | "c" -type T32 = Diff void), Function>; // string | number -type T33 = Filter void), Function>; // () => void +type T30 = Diff<'a' | 'b' | 'c' | 'd', 'a' | 'c' | 'f'>; // "b" | "d" +type T31 = Filter<'a' | 'b' | 'c' | 'd', 'a' | 'c' | 'f'>; // "a" | "c" +type T32 = Diff void), Function>; // string | number +type T33 = Filter void), Function>; // () => void -type NonNullable = Diff; // Remove null and undefined from T +type NonNullable = Diff; // Remove null and undefined from T -type T34 = NonNullable; // string | number -type T35 = NonNullable; // string | string[] +type T34 = NonNullable; // string | number +type T35 = NonNullable; // string | string[] function f1(x: T, y: NonNullable) { - x = y; // Ok - y = x; // Error + x = y; // Ok + y = x; // Error } function f2(x: T, y: NonNullable) { - x = y; // Ok - y = x; // Error - let s1: string = x; // Error - let s2: string = y; // Ok + x = y; // Ok + y = x; // Error + let s1: string = x; // Error + let s2: string = y; // Ok } ``` 有条件类型与映射类型结合时特别有用: ```typescript -type FunctionPropertyNames = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T]; +type FunctionPropertyNames = { + [K in keyof T]: T[K] extends Function ? K : never; +}[keyof T]; type FunctionProperties = Pick>; -type NonFunctionPropertyNames = { [K in keyof T]: T[K] extends Function ? never : K }[keyof T]; +type NonFunctionPropertyNames = { + [K in keyof T]: T[K] extends Function ? never : K; +}[keyof T]; type NonFunctionProperties = Pick>; interface Part { - id: number; - name: string; - subparts: Part[]; - updatePart(newName: string): void; + id: number; + name: string; + subparts: Part[]; + updatePart(newName: string): void; } -type T40 = FunctionPropertyNames; // "updatePart" -type T41 = NonFunctionPropertyNames; // "id" | "name" | "subparts" -type T42 = FunctionProperties; // { updatePart(newName: string): void } -type T43 = NonFunctionProperties; // { id: number, name: string, subparts: Part[] } +type T40 = FunctionPropertyNames; // "updatePart" +type T41 = NonFunctionPropertyNames; // "id" | "name" | "subparts" +type T42 = FunctionProperties; // { updatePart(newName: string): void } +type T43 = NonFunctionProperties; // { id: number, name: string, subparts: Part[] } ``` 与联合类型和交叉类型相似,有条件类型不允许递归地引用自己。比如下面的错误。 @@ -1047,7 +1054,7 @@ type ElementType = T extends any[] ? ElementType : T; #### 有条件类型中的类型推断 -现在在有条件类型的`extends`子语句中,允许出现`infer`声明,它会引入一个待推断的类型变量。 这个推断的类型变量可以在有条件类型的true分支中被引用。 允许出现多个同类型变量的`infer`。 +现在在有条件类型的`extends`子语句中,允许出现`infer`声明,它会引入一个待推断的类型变量。 这个推断的类型变量可以在有条件类型的 true 分支中被引用。 允许出现多个同类型变量的`infer`。 例如,下面代码会提取函数类型的返回值类型: @@ -1058,105 +1065,110 @@ type ReturnType = T extends (...args: any[]) => infer R ? R : any; 有条件类型可以嵌套来构成一系列的匹配模式,按顺序进行求值: ```typescript -type Unpacked = - T extends (infer U)[] ? U : - T extends (...args: any[]) => infer U ? U : - T extends Promise ? U : - T; +type Unpacked = T extends (infer U)[] + ? U + : T extends (...args: any[]) => infer U + ? U + : T extends Promise + ? U + : T; -type T0 = Unpacked; // string -type T1 = Unpacked; // string -type T2 = Unpacked<() => string>; // string -type T3 = Unpacked>; // string -type T4 = Unpacked[]>; // Promise -type T5 = Unpacked[]>>; // string +type T0 = Unpacked; // string +type T1 = Unpacked; // string +type T2 = Unpacked<() => string>; // string +type T3 = Unpacked>; // string +type T4 = Unpacked[]>; // Promise +type T5 = Unpacked[]>>; // string ``` 下面的例子解释了在协变位置上,同一个类型变量的多个候选类型会被推断为联合类型: ```typescript -type Foo = T extends { a: infer U, b: infer U } ? U : never; -type T10 = Foo<{ a: string, b: string }>; // string -type T11 = Foo<{ a: string, b: number }>; // string | number +type Foo = T extends { a: infer U; b: infer U } ? U : never; +type T10 = Foo<{ a: string; b: string }>; // string +type T11 = Foo<{ a: string; b: number }>; // string | number ``` 相似地,在抗变位置上,同一个类型变量的多个候选类型会被推断为交叉类型: ```typescript -type Bar = T extends { a: (x: infer U) => void, b: (x: infer U) => void } ? U : never; -type T20 = Bar<{ a: (x: string) => void, b: (x: string) => void }>; // string -type T21 = Bar<{ a: (x: string) => void, b: (x: number) => void }>; // string & number +type Bar = T extends { a: (x: infer U) => void; b: (x: infer U) => void } + ? U + : never; +type T20 = Bar<{ a: (x: string) => void; b: (x: string) => void }>; // string +type T21 = Bar<{ a: (x: string) => void; b: (x: number) => void }>; // string & number ``` -当推断具有多个调用签名(例如函数重载类型)的类型时,用_最后_的签名(大概是最自由的包含所有情况的签名)进行推断。 无法根据参数类型列表来解析重载。 +当推断具有多个调用签名(例如函数重载类型)的类型时,用*最后*的签名(大概是最自由的包含所有情况的签名)进行推断。 无法根据参数类型列表来解析重载。 ```typescript declare function foo(x: string): number; declare function foo(x: number): string; declare function foo(x: string | number): string | number; -type T30 = ReturnType; // string | number +type T30 = ReturnType; // string | number ``` 无法在正常类型参数的约束子语句中使用`infer`声明: ```typescript -type ReturnType infer R> = R; // 错误,不支持 +type ReturnType infer R> = R; // 错误,不支持 ``` 但是,可以这样达到同样的效果,在约束里删掉类型变量,用有条件类型替换: ```typescript type AnyFunction = (...args: any[]) => any; -type ReturnType = T extends (...args: any[]) => infer R ? R : any; +type ReturnType = T extends (...args: any[]) => infer R + ? R + : any; ``` #### 预定义的有条件类型 -TypeScript 2.8在`lib.d.ts`里增加了一些预定义的有条件类型: +TypeScript 2.8 在`lib.d.ts`里增加了一些预定义的有条件类型: -* `Exclude` -- 从`T`中剔除可以赋值给`U`的类型。 -* `Extract` -- 提取`T`中可以赋值给`U`的类型。 -* `NonNullable` -- 从`T`中剔除`null`和`undefined`。 -* `ReturnType` -- 获取函数返回值类型。 -* `InstanceType` -- 获取构造函数类型的实例类型。 +- `Exclude` -- 从`T`中剔除可以赋值给`U`的类型。 +- `Extract` -- 提取`T`中可以赋值给`U`的类型。 +- `NonNullable` -- 从`T`中剔除`null`和`undefined`。 +- `ReturnType` -- 获取函数返回值类型。 +- `InstanceType` -- 获取构造函数类型的实例类型。 **Example** ```typescript -type T00 = Exclude<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "b" | "d" -type T01 = Extract<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "a" | "c" +type T00 = Exclude<'a' | 'b' | 'c' | 'd', 'a' | 'c' | 'f'>; // "b" | "d" +type T01 = Extract<'a' | 'b' | 'c' | 'd', 'a' | 'c' | 'f'>; // "a" | "c" -type T02 = Exclude void), Function>; // string | number -type T03 = Extract void), Function>; // () => void +type T02 = Exclude void), Function>; // string | number +type T03 = Extract void), Function>; // () => void -type T04 = NonNullable; // string | number -type T05 = NonNullable<(() => string) | string[] | null | undefined>; // (() => string) | string[] +type T04 = NonNullable; // string | number +type T05 = NonNullable<(() => string) | string[] | null | undefined>; // (() => string) | string[] function f1(s: string) { - return { a: 1, b: s }; + return { a: 1, b: s }; } class C { - x = 0; - y = 0; + x = 0; + y = 0; } -type T10 = ReturnType<() => string>; // string -type T11 = ReturnType<(s: string) => void>; // void -type T12 = ReturnType<(() => T)>; // {} -type T13 = ReturnType<(() => T)>; // number[] -type T14 = ReturnType; // { a: number, b: string } -type T15 = ReturnType; // any -type T16 = ReturnType; // never -type T17 = ReturnType; // Error -type T18 = ReturnType; // Error +type T10 = ReturnType<() => string>; // string +type T11 = ReturnType<(s: string) => void>; // void +type T12 = ReturnType<() => T>; // {} +type T13 = ReturnType<() => T>; // number[] +type T14 = ReturnType; // { a: number, b: string } +type T15 = ReturnType; // any +type T16 = ReturnType; // never +type T17 = ReturnType; // Error +type T18 = ReturnType; // Error -type T20 = InstanceType; // C -type T21 = InstanceType; // any -type T22 = InstanceType; // never -type T23 = InstanceType; // Error -type T24 = InstanceType; // Error +type T20 = InstanceType; // C +type T21 = InstanceType; // any +type T22 = InstanceType; // never +type T23 = InstanceType; // Error +type T24 = InstanceType; // Error ``` > 注意:`Exclude`类型是[建议的](https://github.com/Microsoft/TypeScript/issues/12215#issuecomment-307871458)`Diff`类型的一种实现。我们使用`Exclude`这个名字是为了避免破坏已经定义了`Diff`的代码,并且我们感觉这个名字能更好地表达类型的语义。 - diff --git a/zh/reference/declaration-merging.md b/zh/reference/declaration-merging.md index 99b16c55..993d76ec 100644 --- a/zh/reference/declaration-merging.md +++ b/zh/reference/declaration-merging.md @@ -2,23 +2,23 @@ ## 介绍 -TypeScript中有些独特的概念可以在类型层面上描述JavaScript对象的模型。 这其中尤其独特的一个例子是“声明合并”的概念。 理解了这个概念,将有助于操作现有的JavaScript代码。 同时,也会有助于理解更多高级抽象的概念。 +TypeScript 中有些独特的概念可以在类型层面上描述 JavaScript 对象的模型。 这其中尤其独特的一个例子是“声明合并”的概念。 理解了这个概念,将有助于操作现有的 JavaScript 代码。 同时,也会有助于理解更多高级抽象的概念。 对本文件来讲,“声明合并”是指编译器将针对同一个名字的两个独立声明合并为单一声明。 合并后的声明同时拥有原先两个声明的特性。 任何数量的声明都可被合并;不局限于两个声明。 ## 基础概念 -TypeScript中的声明会创建以下三种实体之一:命名空间,类型或值。 创建命名空间的声明会新建一个命名空间,它包含了用(.)符号来访问时使用的名字。 创建类型的声明是:用声明的模型创建一个类型并绑定到给定的名字上。 最后,创建值的声明会创建在JavaScript输出中看到的值。 +TypeScript 中的声明会创建以下三种实体之一:命名空间,类型或值。 创建命名空间的声明会新建一个命名空间,它包含了用(.)符号来访问时使用的名字。 创建类型的声明是:用声明的模型创建一个类型并绑定到给定的名字上。 最后,创建值的声明会创建在 JavaScript 输出中看到的值。 | Declaration Type | Namespace | Type | Value | -| :--- | :---: | :---: | :---: | -| Namespace | X | | X | -| Class | | X | X | -| Enum | | X | X | -| Interface | | X | | -| Type Alias | | X | | -| Function | | | X | -| Variable | | | X | +| :--------------- | :-------: | :--: | :---: | +| Namespace | X | | X | +| Class | | X | X | +| Enum | | X | X | +| Interface | | X | | +| Type Alias | | X | | +| Function | | | X | +| Variable | | | X | 理解每个声明创建了什么,有助于理解当声明合并时有哪些东西被合并了。 @@ -28,15 +28,15 @@ TypeScript中的声明会创建以下三种实体之一:命名空间,类型 ```typescript interface Box { - height: number; - width: number; + height: number; + width: number; } interface Box { - scale: number; + scale: number; } -let box: Box = {height: 5, width: 6, scale: 10}; +let box: Box = { height: 5, width: 6, scale: 10 }; ``` 接口的非函数的成员应该是唯一的。 如果它们不是唯一的,那么它们必须是相同的类型。 如果两个接口中同时声明了同名的非函数成员且它们的类型不同,则编译器会报错。 @@ -47,16 +47,16 @@ let box: Box = {height: 5, width: 6, scale: 10}; ```typescript interface Cloner { - clone(animal: Animal): Animal; + clone(animal: Animal): Animal; } interface Cloner { - clone(animal: Sheep): Sheep; + clone(animal: Sheep): Sheep; } interface Cloner { - clone(animal: Dog): Dog; - clone(animal: Cat): Cat; + clone(animal: Dog): Dog; + clone(animal: Cat): Cat; } ``` @@ -64,30 +64,30 @@ interface Cloner { ```typescript interface Cloner { - clone(animal: Dog): Dog; - clone(animal: Cat): Cat; - clone(animal: Sheep): Sheep; - clone(animal: Animal): Animal; + clone(animal: Dog): Dog; + clone(animal: Cat): Cat; + clone(animal: Sheep): Sheep; + clone(animal: Animal): Animal; } ``` 注意每组接口里的声明顺序保持不变,但各组接口之间的顺序是后来的接口重载出现在靠前位置。 -这个规则有一个例外是当出现特殊的函数签名时。 如果签名里有一个参数的类型是_单一_的字符串字面量(比如,不是字符串字面量的联合类型),那么它将会被提升到重载列表的最顶端。 +这个规则有一个例外是当出现特殊的函数签名时。 如果签名里有一个参数的类型是*单一*的字符串字面量(比如,不是字符串字面量的联合类型),那么它将会被提升到重载列表的最顶端。 比如,下面的接口会合并到一起: ```typescript interface Document { - createElement(tagName: any): Element; + createElement(tagName: any): Element; } interface Document { - createElement(tagName: "div"): HTMLDivElement; - createElement(tagName: "span"): HTMLSpanElement; + createElement(tagName: 'div'): HTMLDivElement; + createElement(tagName: 'span'): HTMLSpanElement; } interface Document { - createElement(tagName: string): HTMLElement; - createElement(tagName: "canvas"): HTMLCanvasElement; + createElement(tagName: string): HTMLElement; + createElement(tagName: 'canvas'): HTMLCanvasElement; } ``` @@ -95,11 +95,11 @@ interface Document { ```typescript interface Document { - createElement(tagName: "canvas"): HTMLCanvasElement; - createElement(tagName: "div"): HTMLDivElement; - createElement(tagName: "span"): HTMLSpanElement; - createElement(tagName: string): HTMLElement; - createElement(tagName: any): Element; + createElement(tagName: 'canvas'): HTMLCanvasElement; + createElement(tagName: 'div'): HTMLDivElement; + createElement(tagName: 'span'): HTMLSpanElement; + createElement(tagName: string): HTMLElement; + createElement(tagName: any): Element; } ``` @@ -115,12 +115,14 @@ interface Document { ```typescript namespace Animals { - export class Zebra { } + export class Zebra {} } namespace Animals { - export interface Legged { numberOfLegs: number; } - export class Dog { } + export interface Legged { + numberOfLegs: number; + } + export class Dog {} } ``` @@ -128,10 +130,12 @@ namespace Animals { ```typescript namespace Animals { - export interface Legged { numberOfLegs: number; } + export interface Legged { + numberOfLegs: number; + } - export class Zebra { } - export class Dog { } + export class Zebra {} + export class Dog {} } ``` @@ -141,17 +145,17 @@ namespace Animals { ```typescript namespace Animal { - let haveMuscles = true; + let haveMuscles = true; - export function animalsHaveMuscles() { - return haveMuscles; - } + export function animalsHaveMuscles() { + return haveMuscles; + } } namespace Animal { - export function doAnimalsHaveMuscles() { - return haveMuscles; // Error, because haveMuscles is not accessible here - } + export function doAnimalsHaveMuscles() { + return haveMuscles; // Error, because haveMuscles is not accessible here + } } ``` @@ -159,7 +163,7 @@ namespace Animal { ## 命名空间与类和函数和枚举类型合并 -命名空间可以与其它类型的声明进行合并。 只要命名空间的定义符合将要合并类型的定义。合并结果包含两者的声明类型。 TypeScript使用这个功能去实现一些JavaScript里的设计模式。 +命名空间可以与其它类型的声明进行合并。 只要命名空间的定义符合将要合并类型的定义。合并结果包含两者的声明类型。 TypeScript 使用这个功能去实现一些 JavaScript 里的设计模式。 ### 合并命名空间和类 @@ -167,101 +171,97 @@ namespace Animal { ```typescript class Album { - label: Album.AlbumLabel; + label: Album.AlbumLabel; } namespace Album { - export class AlbumLabel { } + export class AlbumLabel {} } ``` 合并规则与上面`合并命名空间`小节里讲的规则一致,我们必须导出`AlbumLabel`类,好让合并的类能访问。 合并结果是一个类并带有一个内部类。 你也可以使用命名空间为类增加一些静态属性。 -除了内部类的模式,你在JavaScript里,创建一个函数稍后扩展它增加一些属性也是很常见的。 TypeScript使用声明合并来达到这个目的并保证类型安全。 +除了内部类的模式,你在 JavaScript 里,创建一个函数稍后扩展它增加一些属性也是很常见的。 TypeScript 使用声明合并来达到这个目的并保证类型安全。 ```typescript function buildLabel(name: string): string { - return buildLabel.prefix + name + buildLabel.suffix; + return buildLabel.prefix + name + buildLabel.suffix; } namespace buildLabel { - export let suffix = ""; - export let prefix = "Hello, "; + export let suffix = ''; + export let prefix = 'Hello, '; } -console.log(buildLabel("Sam Smith")); +console.log(buildLabel('Sam Smith')); ``` 相似的,命名空间可以用来扩展枚举型: ```typescript enum Color { - red = 1, - green = 2, - blue = 4 + red = 1, + green = 2, + blue = 4, } namespace Color { - export function mixColor(colorName: string) { - if (colorName == "yellow") { - return Color.red + Color.green; - } - else if (colorName == "white") { - return Color.red + Color.green + Color.blue; - } - else if (colorName == "magenta") { - return Color.red + Color.blue; - } - else if (colorName == "cyan") { - return Color.green + Color.blue; - } + export function mixColor(colorName: string) { + if (colorName == 'yellow') { + return Color.red + Color.green; + } else if (colorName == 'white') { + return Color.red + Color.green + Color.blue; + } else if (colorName == 'magenta') { + return Color.red + Color.blue; + } else if (colorName == 'cyan') { + return Color.green + Color.blue; } + } } ``` ## 非法的合并 -TypeScript并非允许所有的合并。 目前,类不能与其它类或变量合并。 想要了解如何模仿类的合并,请参考[TypeScript的混入](mixins.md)。 +TypeScript 并非允许所有的合并。 目前,类不能与其它类或变量合并。 想要了解如何模仿类的合并,请参考[TypeScript 的混入](mixins.md)。 ## 模块扩展 -虽然JavaScript不支持合并,但你可以为导入的对象打补丁以更新它们。让我们考察一下这个玩具性的示例: +虽然 JavaScript 不支持合并,但你可以为导入的对象打补丁以更新它们。让我们考察一下这个玩具性的示例: ```typescript // observable.ts export class Observable { - // ... implementation left as an exercise for the reader ... + // ... implementation left as an exercise for the reader ... } // map.ts -import { Observable } from "./observable"; +import { Observable } from './observable'; Observable.prototype.map = function (f) { - // ... another exercise for the reader -} + // ... another exercise for the reader +}; ``` -它也可以很好地工作在TypeScript中, 但编译器对 `Observable.prototype.map`一无所知。 你可以使用扩展模块来将它告诉编译器: +它也可以很好地工作在 TypeScript 中, 但编译器对 `Observable.prototype.map`一无所知。 你可以使用扩展模块来将它告诉编译器: ```typescript // observable.ts export class Observable { - // ... implementation left as an exercise for the reader ... + // ... implementation left as an exercise for the reader ... } // map.ts -import { Observable } from "./observable"; -declare module "./observable" { - interface Observable { - map(f: (x: T) => U): Observable; - } +import { Observable } from './observable'; +declare module './observable' { + interface Observable { + map(f: (x: T) => U): Observable; + } } Observable.prototype.map = function (f) { - // ... another exercise for the reader -} - + // ... another exercise for the reader +}; // consumer.ts -import { Observable } from "./observable"; -import "./map"; +import { Observable } from './observable'; +import './map'; let o: Observable; o.map(x => x.toFixed()); ``` @@ -278,19 +278,18 @@ o.map(x => x.toFixed()); ```typescript // observable.ts export class Observable { - // ... still no implementation ... + // ... still no implementation ... } declare global { - interface Array { - toObservable(): Observable; - } + interface Array { + toObservable(): Observable; + } } Array.prototype.toObservable = function () { - // ... -} + // ... +}; ``` 全局扩展与模块扩展的行为和限制是相同的。 - diff --git a/zh/reference/decorators.md b/zh/reference/decorators.md index 3e2efab8..9acab303 100644 --- a/zh/reference/decorators.md +++ b/zh/reference/decorators.md @@ -2,9 +2,9 @@ ## 介绍 -随着TypeScript和ES6里引入了类,在一些场景下我们需要额外的特性来支持标注或修改类及其成员。 装饰器(Decorators)为我们在类的声明及成员上通过元编程语法添加标注提供了一种方式。 Javascript里的装饰器目前处在[建议征集的第二阶段](https://github.com/tc39/proposal-decorators),但在TypeScript里已做为一项实验性特性予以支持。 +随着 TypeScript 和 ES6 里引入了类,在一些场景下我们需要额外的特性来支持标注或修改类及其成员。 装饰器(Decorators)为我们在类的声明及成员上通过元编程语法添加标注提供了一种方式。 Javascript 里的装饰器目前处在[建议征集的第二阶段](https://github.com/tc39/proposal-decorators),但在 TypeScript 里已做为一项实验性特性予以支持。 -> 注意  装饰器是一项实验性特性,在未来的版本中可能会发生改变。 +> 注意   装饰器是一项实验性特性,在未来的版本中可能会发生改变。 若要启用实验性的装饰器特性,你必须在命令行或`tsconfig.json`里启用`experimentalDecorators`编译器选项: @@ -27,45 +27,47 @@ tsc --target ES5 --experimentalDecorators ## 装饰器 -_装饰器_是一种特殊类型的声明,它能够被附加到[类声明](decorators.md#class-decorators),[方法](decorators.md#method-decorators),[访问符](decorators.md#accessor-decorators),[属性](decorators.md#property-decorators)或[参数](decorators.md#parameter-decorators)上。 装饰器使用`@expression`这种形式,`expression`求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。 +*装饰器*是一种特殊类型的声明,它能够被附加到[类声明](decorators.md#class-decorators),[方法](decorators.md#method-decorators),[访问符](decorators.md#accessor-decorators),[属性](decorators.md#property-decorators)或[参数](decorators.md#parameter-decorators)上。 装饰器使用`@expression`这种形式,`expression`求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。 例如,有一个`@sealed`装饰器,我们会这样定义`sealed`函数: ```typescript function sealed(target) { - // do something with "target" ... + // do something with "target" ... } ``` -> 注意  后面[类装饰器](decorators.md#class-decorators)小节里有一个更加详细的例子。 +> 注意   后面[类装饰器](decorators.md#class-decorators)小节里有一个更加详细的例子。 ### 装饰器工厂 -如果我们要定制一个修饰器如何应用到一个声明上,我们得写一个装饰器工厂函数。 _装饰器工厂_就是一个简单的函数,它返回一个表达式,以供装饰器在运行时调用。 +如果我们要定制一个修饰器如何应用到一个声明上,我们得写一个装饰器工厂函数。 *装饰器工厂*就是一个简单的函数,它返回一个表达式,以供装饰器在运行时调用。 我们可以通过下面的方式来写一个装饰器工厂函数: ```typescript -function color(value: string) { // 这是一个装饰器工厂 - return function (target) { // 这是装饰器 - // do something with "target" and "value"... - } +function color(value: string) { + // 这是一个装饰器工厂 + return function (target) { + // 这是装饰器 + // do something with "target" and "value"... + }; } ``` -> 注意  下面[方法装饰器](decorators.md#method-decorators)小节里有一个更加详细的例子。 +> 注意   下面[方法装饰器](decorators.md#method-decorators)小节里有一个更加详细的例子。 ### 装饰器组合 多个装饰器可以同时应用到一个声明上,就像下面的示例: -* 书写在同一行上: +- 书写在同一行上: ```typescript @f @g x ``` -* 书写在多行上: +- 书写在多行上: ```typescript @f @@ -73,9 +75,9 @@ function color(value: string) { // 这是一个装饰器工厂 x ``` -当多个装饰器应用于一个声明上,它们求值方式与[复合函数](http://en.wikipedia.org/wiki/Function_composition)相似。在这个模型下,当复合_f_和_g_时,复合的结果\(_f_ ∘ _g_\)\(_x_\)等同于_f_\(_g_\(_x_\)\)。 +当多个装饰器应用于一个声明上,它们求值方式与[复合函数](http://en.wikipedia.org/wiki/Function_composition)相似。在这个模型下,当复合*f*和*g*时,复合的结果\(_f_ ∘ _g_\)\(_x_\)等同于*f*\(_g_\(_x_\)\)。 -同样的,在TypeScript里,当多个装饰器应用在一个声明上时会进行如下步骤的操作: +同样的,在 TypeScript 里,当多个装饰器应用在一个声明上时会进行如下步骤的操作: 1. 由上至下依次对装饰器表达式求值。 2. 求值的结果会被当作函数,由下至上依次调用。 @@ -84,23 +86,31 @@ x ```typescript function f() { - console.log("f(): evaluated"); - return function (target, propertyKey: string, descriptor: PropertyDescriptor) { - console.log("f(): called"); - } + console.log('f(): evaluated'); + return function ( + target, + propertyKey: string, + descriptor: PropertyDescriptor + ) { + console.log('f(): called'); + }; } function g() { - console.log("g(): evaluated"); - return function (target, propertyKey: string, descriptor: PropertyDescriptor) { - console.log("g(): called"); - } + console.log('g(): evaluated'); + return function ( + target, + propertyKey: string, + descriptor: PropertyDescriptor + ) { + console.log('g(): called'); + }; } class C { - @f() - @g() - method() {} + @f() + @g() + method() {} } ``` @@ -117,33 +127,33 @@ f(): called 类中不同声明上的装饰器将按以下规定的顺序应用: -1. _参数装饰器_,然后依次是_方法装饰器_,_访问符装饰器_,或_属性装饰器_应用到每个实例成员。 -2. _参数装饰器_,然后依次是_方法装饰器_,_访问符装饰器_,或_属性装饰器_应用到每个静态成员。 -3. _参数装饰器_应用到构造函数。 -4. _类装饰器_应用到类。 +1. _参数装饰器_,然后依次是*方法装饰器*,_访问符装饰器_,或*属性装饰器*应用到每个实例成员。 +2. _参数装饰器_,然后依次是*方法装饰器*,_访问符装饰器_,或*属性装饰器*应用到每个静态成员。 +3. *参数装饰器*应用到构造函数。 +4. *类装饰器*应用到类。 ### 类装饰器 -_类装饰器_在类声明之前被声明(紧靠着类声明)。 类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。 类装饰器不能用在声明文件中\(`.d.ts`\),也不能用在任何外部上下文中(比如`declare`的类)。 +*类装饰器*在类声明之前被声明(紧靠着类声明)。 类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。 类装饰器不能用在声明文件中\(`.d.ts`\),也不能用在任何外部上下文中(比如`declare`的类)。 类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。 如果类装饰器返回一个值,它会使用提供的构造函数来替换类的声明。 -> 注意  如果你要返回一个新的构造函数,你必须注意处理好原来的原型链。 在运行时的装饰器调用逻辑中_不会_为你做这些。 +> 注意   如果你要返回一个新的构造函数,你必须注意处理好原来的原型链。 在运行时的装饰器调用逻辑中*不会*为你做这些。 下面是使用类装饰器\(`@sealed`\)的例子,应用在`Greeter`类: ```typescript @sealed class Greeter { - greeting: string; - constructor(message: string) { - this.greeting = message; - } - greet() { - return "Hello, " + this.greeting; - } + greeting: string; + constructor(message: string) { + this.greeting = message; + } + greet() { + return 'Hello, ' + this.greeting; + } } ``` @@ -151,8 +161,8 @@ class Greeter { ```typescript function sealed(constructor: Function) { - Object.seal(constructor); - Object.seal(constructor.prototype); + Object.seal(constructor); + Object.seal(constructor.prototype); } ``` @@ -161,54 +171,56 @@ function sealed(constructor: Function) { 下面是一个重载构造函数的例子。 ```typescript -function classDecorator(constructor:T) { - return class extends constructor { - newProperty = "new property"; - hello = "override"; - } +function classDecorator( + constructor: T +) { + return class extends constructor { + newProperty = 'new property'; + hello = 'override'; + }; } @classDecorator class Greeter { - property = "property"; - hello: string; - constructor(m: string) { - this.hello = m; - } + property = 'property'; + hello: string; + constructor(m: string) { + this.hello = m; + } } -console.log(new Greeter("world")); +console.log(new Greeter('world')); ``` ### 方法装饰器 -_方法装饰器_声明在一个方法的声明之前(紧靠着方法声明)。 它会被应用到方法的_属性描述符_上,可以用来监视,修改或者替换方法定义。 方法装饰器不能用在声明文件\(`.d.ts`\),重载或者任何外部上下文(比如`declare`的类)中。 +*方法装饰器*声明在一个方法的声明之前(紧靠着方法声明)。 它会被应用到方法的*属性描述符*上,可以用来监视,修改或者替换方法定义。 方法装饰器不能用在声明文件\(`.d.ts`\),重载或者任何外部上下文(比如`declare`的类)中。 -方法装饰器表达式会在运行时当作函数被调用,传入下列3个参数: +方法装饰器表达式会在运行时当作函数被调用,传入下列 3 个参数: 1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。 2. 成员的名字。 -3. 成员的_属性描述符_。 +3. 成员的*属性描述符*。 -> 注意  如果代码输出目标版本小于`ES5`,_属性描述符_将会是`undefined`。 +> 注意   如果代码输出目标版本小于`ES5`,*属性描述符*将会是`undefined`。 -如果方法装饰器返回一个值,它会被用作方法的_属性描述符_。 +如果方法装饰器返回一个值,它会被用作方法的*属性描述符*。 -> 注意  如果代码输出目标版本小于`ES5`返回值会被忽略。 +> 注意   如果代码输出目标版本小于`ES5`返回值会被忽略。 下面是一个方法装饰器(`@enumerable`)的例子,应用于`Greeter`类的方法上: ```typescript class Greeter { - greeting: string; - constructor(message: string) { - this.greeting = message; - } - - @enumerable(false) - greet() { - return "Hello, " + this.greeting; - } + greeting: string; + constructor(message: string) { + this.greeting = message; + } + + @enumerable(false) + greet() { + return 'Hello, ' + this.greeting; + } } ``` @@ -216,9 +228,13 @@ class Greeter { ```typescript function enumerable(value: boolean) { - return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { - descriptor.enumerable = value; - }; + return function ( + target: any, + propertyKey: string, + descriptor: PropertyDescriptor + ) { + descriptor.enumerable = value; + }; } ``` @@ -226,38 +242,42 @@ function enumerable(value: boolean) { ### 访问器装饰器 -_访问器装饰器_声明在一个访问器的声明之前(紧靠着访问器声明)。 访问器装饰器应用于访问器的_属性描述符_并且可以用来监视,修改或替换一个访问器的定义。 访问器装饰器不能用在声明文件中(.d.ts),或者任何外部上下文(比如`declare`的类)里。 +*访问器装饰器*声明在一个访问器的声明之前(紧靠着访问器声明)。 访问器装饰器应用于访问器的*属性描述符*并且可以用来监视,修改或替换一个访问器的定义。 访问器装饰器不能用在声明文件中(.d.ts),或者任何外部上下文(比如`declare`的类)里。 -> 注意  TypeScript不允许同时装饰一个成员的`get`和`set`访问器。取而代之的是,一个成员的所有装饰的必须应用在文档顺序的第一个访问器上。这是因为,在装饰器应用于一个_属性描述符_时,它联合了`get`和`set`访问器,而不是分开声明的。 +> 注意   TypeScript 不允许同时装饰一个成员的`get`和`set`访问器。取而代之的是,一个成员的所有装饰的必须应用在文档顺序的第一个访问器上。这是因为,在装饰器应用于一个*属性描述符*时,它联合了`get`和`set`访问器,而不是分开声明的。 -访问器装饰器表达式会在运行时当作函数被调用,传入下列3个参数: +访问器装饰器表达式会在运行时当作函数被调用,传入下列 3 个参数: 1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。 2. 成员的名字。 -3. 成员的_属性描述符_。 +3. 成员的*属性描述符*。 -> 注意  如果代码输出目标版本小于`ES5`,_Property Descriptor_将会是`undefined`。 +> 注意   如果代码输出目标版本小于`ES5`,*Property Descriptor*将会是`undefined`。 -如果访问器装饰器返回一个值,它会被用作方法的_属性描述符_。 +如果访问器装饰器返回一个值,它会被用作方法的*属性描述符*。 -> 注意  如果代码输出目标版本小于`ES5`返回值会被忽略。 +> 注意   如果代码输出目标版本小于`ES5`返回值会被忽略。 下面是使用了访问器装饰器(`@configurable`)的例子,应用于`Point`类的成员上: ```typescript class Point { - private _x: number; - private _y: number; - constructor(x: number, y: number) { - this._x = x; - this._y = y; - } - - @configurable(false) - get x() { return this._x; } - - @configurable(false) - get y() { return this._y; } + private _x: number; + private _y: number; + constructor(x: number, y: number) { + this._x = x; + this._y = y; + } + + @configurable(false) + get x() { + return this._x; + } + + @configurable(false) + get y() { + return this._y; + } } ``` @@ -265,73 +285,77 @@ class Point { ```typescript function configurable(value: boolean) { - return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { - descriptor.configurable = value; - }; + return function ( + target: any, + propertyKey: string, + descriptor: PropertyDescriptor + ) { + descriptor.configurable = value; + }; } ``` ### 属性装饰器 -_属性装饰器_声明在一个属性声明之前(紧靠着属性声明)。 属性装饰器不能用在声明文件中(.d.ts),或者任何外部上下文(比如`declare`的类)里。 +*属性装饰器*声明在一个属性声明之前(紧靠着属性声明)。 属性装饰器不能用在声明文件中(.d.ts),或者任何外部上下文(比如`declare`的类)里。 -属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数: +属性装饰器表达式会在运行时当作函数被调用,传入下列 2 个参数: 1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。 2. 成员的名字。 -> 注意  _属性描述符_不会做为参数传入属性装饰器,这与TypeScript是如何初始化属性装饰器的有关。 因为目前没有办法在定义一个原型对象的成员时描述一个实例属性,并且没办法监视或修改一个属性的初始化方法。返回值也会被忽略。 因此,属性描述符只能用来监视类中是否声明了某个名字的属性。 +> 注意   *属性描述符*不会做为参数传入属性装饰器,这与 TypeScript 是如何初始化属性装饰器的有关。 因为目前没有办法在定义一个原型对象的成员时描述一个实例属性,并且没办法监视或修改一个属性的初始化方法。返回值也会被忽略。 因此,属性描述符只能用来监视类中是否声明了某个名字的属性。 -如果访问符装饰器返回一个值,它会被用作方法的_属性描述符_。 +如果访问符装饰器返回一个值,它会被用作方法的*属性描述符*。 我们可以用它来记录这个属性的元数据,如下例所示: ```typescript class Greeter { - @format("Hello, %s") - greeting: string; - - constructor(message: string) { - this.greeting = message; - } - greet() { - let formatString = getFormat(this, "greeting"); - return formatString.replace("%s", this.greeting); - } + @format('Hello, %s') + greeting: string; + + constructor(message: string) { + this.greeting = message; + } + greet() { + let formatString = getFormat(this, 'greeting'); + return formatString.replace('%s', this.greeting); + } } ``` 然后定义`@format`装饰器和`getFormat`函数: ```typescript -import "reflect-metadata"; +import 'reflect-metadata'; -const formatMetadataKey = Symbol("format"); +const formatMetadataKey = Symbol('format'); function format(formatString: string) { - return Reflect.metadata(formatMetadataKey, formatString); + return Reflect.metadata(formatMetadataKey, formatString); } function getFormat(target: any, propertyKey: string) { - return Reflect.getMetadata(formatMetadataKey, target, propertyKey); + return Reflect.getMetadata(formatMetadataKey, target, propertyKey); } ``` 这个`@format("Hello, %s")`装饰器是个 [装饰器工厂](decorators.md#decorator-factories)。 当`@format("Hello, %s")`被调用时,它添加一条这个属性的元数据,通过`reflect-metadata`库里的`Reflect.metadata`函数。 当`getFormat`被调用时,它读取格式的元数据。 -> 注意  这个例子需要使用`reflect-metadata`库。 查看[元数据](decorators.md#metadata)了解`reflect-metadata`库更详细的信息。 +> 注意   这个例子需要使用`reflect-metadata`库。 查看[元数据](decorators.md#metadata)了解`reflect-metadata`库更详细的信息。 ### 参数装饰器 -_参数装饰器_声明在一个参数声明之前(紧靠着参数声明)。 参数装饰器应用于类构造函数或方法声明。 参数装饰器不能用在声明文件(.d.ts),重载或其它外部上下文(比如`declare`的类)里。 +*参数装饰器*声明在一个参数声明之前(紧靠着参数声明)。 参数装饰器应用于类构造函数或方法声明。 参数装饰器不能用在声明文件(.d.ts),重载或其它外部上下文(比如`declare`的类)里。 -参数装饰器表达式会在运行时当作函数被调用,传入下列3个参数: +参数装饰器表达式会在运行时当作函数被调用,传入下列 3 个参数: 1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。 2. 成员的名字。 3. 参数在函数参数列表中的索引。 -> 注意  参数装饰器只能用来监视一个方法的参数是否被传入。 +> 注意   参数装饰器只能用来监视一个方法的参数是否被传入。 参数装饰器的返回值会被忽略。 @@ -339,64 +363,85 @@ _参数装饰器_声明在一个参数声明之前(紧靠着参数声明)。 ```typescript class Greeter { - greeting: string; + greeting: string; - constructor(message: string) { - this.greeting = message; - } + constructor(message: string) { + this.greeting = message; + } - @validate - greet(@required name: string) { - return "Hello " + name + ", " + this.greeting; - } + @validate + greet(@required name: string) { + return 'Hello ' + name + ', ' + this.greeting; + } } ``` 然后我们使用下面的函数定义 `@required` 和 `@validate` 装饰器: ```typescript -import "reflect-metadata"; - -const requiredMetadataKey = Symbol("required"); - -function required(target: Object, propertyKey: string | symbol, parameterIndex: number) { - let existingRequiredParameters: number[] = Reflect.getOwnMetadata(requiredMetadataKey, target, propertyKey) || []; - existingRequiredParameters.push(parameterIndex); - Reflect.defineMetadata(requiredMetadataKey, existingRequiredParameters, target, propertyKey); +import 'reflect-metadata'; + +const requiredMetadataKey = Symbol('required'); + +function required( + target: Object, + propertyKey: string | symbol, + parameterIndex: number +) { + let existingRequiredParameters: number[] = + Reflect.getOwnMetadata(requiredMetadataKey, target, propertyKey) || []; + existingRequiredParameters.push(parameterIndex); + Reflect.defineMetadata( + requiredMetadataKey, + existingRequiredParameters, + target, + propertyKey + ); } -function validate(target: any, propertyName: string, descriptor: TypedPropertyDescriptor) { - let method = descriptor.value; - descriptor.value = function () { - let requiredParameters: number[] = Reflect.getOwnMetadata(requiredMetadataKey, target, propertyName); - if (requiredParameters) { - for (let parameterIndex of requiredParameters) { - if (parameterIndex >= arguments.length || arguments[parameterIndex] === undefined) { - throw new Error("Missing required argument."); - } - } +function validate( + target: any, + propertyName: string, + descriptor: TypedPropertyDescriptor +) { + let method = descriptor.value; + descriptor.value = function () { + let requiredParameters: number[] = Reflect.getOwnMetadata( + requiredMetadataKey, + target, + propertyName + ); + if (requiredParameters) { + for (let parameterIndex of requiredParameters) { + if ( + parameterIndex >= arguments.length || + arguments[parameterIndex] === undefined + ) { + throw new Error('Missing required argument.'); } - - return method.apply(this, arguments); + } } + + return method.apply(this, arguments); + }; } ``` `@required`装饰器添加了元数据实体把参数标记为必需的。 `@validate`装饰器把`greet`方法包裹在一个函数里在调用原先的函数前验证函数参数。 -> 注意  这个例子使用了`reflect-metadata`库。 查看[元数据](decorators.md#metadata)了解`reflect-metadata`库的更多信息。 +> 注意   这个例子使用了`reflect-metadata`库。 查看[元数据](decorators.md#metadata)了解`reflect-metadata`库的更多信息。 ### 元数据 -一些例子使用了`reflect-metadata`库来支持[实验性的metadata API](https://github.com/rbuckton/ReflectDecorators)。 这个库还不是ECMAScript \(JavaScript\)标准的一部分。 然而,当装饰器被ECMAScript官方标准采纳后,这些扩展也将被推荐给ECMAScript以采纳。 +一些例子使用了`reflect-metadata`库来支持[实验性的 metadata API](https://github.com/rbuckton/ReflectDecorators)。 这个库还不是 ECMAScript \(JavaScript\)标准的一部分。 然而,当装饰器被 ECMAScript 官方标准采纳后,这些扩展也将被推荐给 ECMAScript 以采纳。 -你可以通过npm安装这个库: +你可以通过 npm 安装这个库: ```text npm i reflect-metadata --save ``` -TypeScript支持为带有装饰器的声明生成元数据。 你需要在命令行或`tsconfig.json`里启用`emitDecoratorMetadata`编译器选项。 +TypeScript 支持为带有装饰器的声明生成元数据。 你需要在命令行或`tsconfig.json`里启用`emitDecoratorMetadata`编译器选项。 **Command Line**: @@ -421,56 +466,75 @@ tsc --target ES5 --experimentalDecorators --emitDecoratorMetadata 如下例所示: ```typescript -import "reflect-metadata"; +import 'reflect-metadata'; class Point { - x: number; - y: number; + x: number; + y: number; } class Line { - private _p0: Point; - private _p1: Point; - - @validate - set p0(value: Point) { this._p0 = value; } - get p0() { return this._p0; } - - @validate - set p1(value: Point) { this._p1 = value; } - get p1() { return this._p1; } + private _p0: Point; + private _p1: Point; + + @validate + set p0(value: Point) { + this._p0 = value; + } + get p0() { + return this._p0; + } + + @validate + set p1(value: Point) { + this._p1 = value; + } + get p1() { + return this._p1; + } } -function validate(target: any, propertyKey: string, descriptor: TypedPropertyDescriptor) { - let set = descriptor.set; - descriptor.set = function (value: T) { - let type = Reflect.getMetadata("design:type", target, propertyKey); - if (!(value instanceof type)) { - throw new TypeError("Invalid type."); - } - set.call(target, value); +function validate( + target: any, + propertyKey: string, + descriptor: TypedPropertyDescriptor +) { + let set = descriptor.set; + descriptor.set = function (value: T) { + let type = Reflect.getMetadata('design:type', target, propertyKey); + if (!(value instanceof type)) { + throw new TypeError('Invalid type.'); } + set.call(target, value); + }; } ``` -TypeScript编译器可以通过`@Reflect.metadata`装饰器注入设计阶段的类型信息。 你可以认为它相当于下面的TypeScript: +TypeScript 编译器可以通过`@Reflect.metadata`装饰器注入设计阶段的类型信息。 你可以认为它相当于下面的 TypeScript: ```typescript class Line { - private _p0: Point; - private _p1: Point; - - @validate - @Reflect.metadata("design:type", Point) - set p0(value: Point) { this._p0 = value; } - get p0() { return this._p0; } - - @validate - @Reflect.metadata("design:type", Point) - set p1(value: Point) { this._p1 = value; } - get p1() { return this._p1; } + private _p0: Point; + private _p1: Point; + + @validate + @Reflect.metadata('design:type', Point) + set p0(value: Point) { + this._p0 = value; + } + get p0() { + return this._p0; + } + + @validate + @Reflect.metadata('design:type', Point) + set p1(value: Point) { + this._p1 = value; + } + get p1() { + return this._p1; + } } ``` -> 注意  装饰器元数据是个实验性的特性并且可能在以后的版本中发生破坏性的改变(breaking changes)。 - +> 注意   装饰器元数据是个实验性的特性并且可能在以后的版本中发生破坏性的改变(breaking changes)。 diff --git a/zh/reference/iterators-and-generators.md b/zh/reference/iterators-and-generators.md index b0ff0b08..ea0b7e87 100644 --- a/zh/reference/iterators-and-generators.md +++ b/zh/reference/iterators-and-generators.md @@ -7,10 +7,10 @@ `for..of`会遍历可迭代的对象,调用对象上的`Symbol.iterator`方法。 下面是在数组上使用`for..of`的简单例子: ```typescript -let someArray = [1, "string", false]; +let someArray = [1, 'string', false]; for (let entry of someArray) { - console.log(entry); // 1, "string", false + console.log(entry); // 1, "string", false } ``` @@ -24,26 +24,26 @@ for (let entry of someArray) { let list = [4, 5, 6]; for (let i in list) { - console.log(i); // "0", "1", "2", + console.log(i); // "0", "1", "2", } for (let i of list) { - console.log(i); // "4", "5", "6" + console.log(i); // "4", "5", "6" } ``` 另一个区别是`for..in`可以操作任何对象;它提供了查看对象属性的一种方法。 但是`for..of`关注于迭代对象的值。内置对象`Map`和`Set`已经实现了`Symbol.iterator`方法,让我们可以访问它们保存的值。 ```typescript -let pets = new Set(["Cat", "Dog", "Hamster"]); -pets["species"] = "mammals"; +let pets = new Set(['Cat', 'Dog', 'Hamster']); +pets['species'] = 'mammals'; for (let pet in pets) { - console.log(pet); // "species" + console.log(pet); // "species" } for (let pet of pets) { - console.log(pet); // "Cat", "Dog", "Hamster" + console.log(pet); // "Cat", "Dog", "Hamster" } ``` @@ -51,14 +51,14 @@ for (let pet of pets) { #### 目标为 ES5 和 ES3 -当生成目标为ES5或ES3,迭代器只允许在`Array`类型上使用。 在非数组值上使用`for..of`语句会得到一个错误,就算这些非数组值已经实现了`Symbol.iterator`属性。 +当生成目标为 ES5 或 ES3,迭代器只允许在`Array`类型上使用。 在非数组值上使用`for..of`语句会得到一个错误,就算这些非数组值已经实现了`Symbol.iterator`属性。 编译器会生成一个简单的`for`循环做为`for..of`循环,比如: ```typescript let numbers = [1, 2, 3]; for (let num of numbers) { - console.log(num); + console.log(num); } ``` @@ -67,12 +67,11 @@ for (let num of numbers) { ```javascript var numbers = [1, 2, 3]; for (var _i = 0; _i < numbers.length; _i++) { - var num = numbers[_i]; - console.log(num); + var num = numbers[_i]; + console.log(num); } ``` #### 目标为 ECMAScript 2015 或更高 -当目标为兼容ECMAScipt 2015的引擎时,编译器会生成相应引擎的`for..of`内置迭代器实现方式。 - +当目标为兼容 ECMAScipt 2015 的引擎时,编译器会生成相应引擎的`for..of`内置迭代器实现方式。 diff --git a/zh/reference/jsx.md b/zh/reference/jsx.md index 1f05dd44..307101dc 100644 --- a/zh/reference/jsx.md +++ b/zh/reference/jsx.md @@ -2,26 +2,26 @@ ## 介绍 -[JSX](https://facebook.github.io/jsx/)是一种嵌入式的类似XML的语法。 它可以被转换成合法的JavaScript,尽管转换的语义是依据不同的实现而定的。 JSX因[React](https://reactjs.org/)框架而流行,但也存在其它的实现。 TypeScript支持内嵌,类型检查以及将JSX直接编译为JavaScript。 +[JSX](https://facebook.github.io/jsx/)是一种嵌入式的类似 XML 的语法。 它可以被转换成合法的 JavaScript,尽管转换的语义是依据不同的实现而定的。 JSX 因[React](https://reactjs.org/)框架而流行,但也存在其它的实现。 TypeScript 支持内嵌,类型检查以及将 JSX 直接编译为 JavaScript。 ## 基本用法 -想要使用JSX必须做两件事: +想要使用 JSX 必须做两件事: 1. 给文件一个`.tsx`扩展名 2. 启用`jsx`选项 -TypeScript具有三种JSX模式:`preserve`,`react`和`react-native`。 这些模式只在代码生成阶段起作用 - 类型检查并不受影响。 在`preserve`模式下生成代码中会保留JSX以供后续的转换操作使用(比如:[Babel](https://babeljs.io/))。 另外,输出文件会带有`.jsx`扩展名。 `react`模式会生成`React.createElement`,在使用前不需要再进行转换操作了,输出文件的扩展名为`.js`。 `react-native`相当于`preserve`,它也保留了所有的JSX,但是输出文件的扩展名是`.js`。 +TypeScript 具有三种 JSX 模式:`preserve`,`react`和`react-native`。 这些模式只在代码生成阶段起作用 - 类型检查并不受影响。 在`preserve`模式下生成代码中会保留 JSX 以供后续的转换操作使用(比如:[Babel](https://babeljs.io/))。 另外,输出文件会带有`.jsx`扩展名。 `react`模式会生成`React.createElement`,在使用前不需要再进行转换操作了,输出文件的扩展名为`.js`。 `react-native`相当于`preserve`,它也保留了所有的 JSX,但是输出文件的扩展名是`.js`。 -| 模式 | 输入 | 输出 | 输出文件扩展名 | -| :--- | :--- | :--- | :--- | -| `preserve` | `
` | `
` | `.jsx` | -| `react` | `
` | `React.createElement("div")` | `.js` | -| `react-native` | `
` | `
` | `.js` | +| 模式 | 输入 | 输出 | 输出文件扩展名 | +| :------------- | :-------- | :--------------------------- | :------------- | +| `preserve` | `
` | `
` | `.jsx` | +| `react` | `
` | `React.createElement("div")` | `.js` | +| `react-native` | `
` | `
` | `.js` | 你可以通过在命令行里使用`--jsx`标记或[tsconfig.json](../project-config/tsconfig.json.md)里的选项来指定模式。 -> \*注意:当输出目标为`react JSX`时,你可以使用`--jsxFactory`指定JSX工厂函数(默认值为`React.createElement`) +> \*注意:当输出目标为`react JSX`时,你可以使用`--jsxFactory`指定 JSX 工厂函数(默认值为`React.createElement`) ## `as`操作符 @@ -31,7 +31,7 @@ TypeScript具有三种JSX模式:`preserve`,`react`和`react-native`。 这 var foo = bar; ``` -这里断言`bar`变量是`foo`类型的。 因为TypeScript也使用尖括号来表示类型断言,在结合JSX的语法后将带来解析上的困难。因此,TypeScript在`.tsx`文件里禁用了使用尖括号的类型断言。 +这里断言`bar`变量是`foo`类型的。 因为 TypeScript 也使用尖括号来表示类型断言,在结合 JSX 的语法后将带来解析上的困难。因此,TypeScript 在`.tsx`文件里禁用了使用尖括号的类型断言。 由于不能够在`.tsx`文件里使用上述语法,因此我们应该使用另一个类型断言操作符:`as`。 上面的例子可以很容易地使用`as`操作符改写: @@ -43,14 +43,14 @@ var foo = bar as foo; ## 类型检查 -为了理解JSX的类型检查,你必须首先理解固有元素与基于值的元素之间的区别。 假设有这样一个JSX表达式``,`expr`可能引用环境自带的某些东西(比如,在DOM环境里的`div`或`span`)或者是你自定义的组件。 这是非常重要的,原因有如下两点: +为了理解 JSX 的类型检查,你必须首先理解固有元素与基于值的元素之间的区别。 假设有这样一个 JSX 表达式``,`expr`可能引用环境自带的某些东西(比如,在 DOM 环境里的`div`或`span`)或者是你自定义的组件。 这是非常重要的,原因有如下两点: -1. 对于React,固有元素会生成字符串(`React.createElement("div")`),然而由你自定义的组件却不会生成(`React.createElement(MyComponent)`)。 -2. 传入JSX元素里的属性类型的查找方式不同。 +1. 对于 React,固有元素会生成字符串(`React.createElement("div")`),然而由你自定义的组件却不会生成(`React.createElement(MyComponent)`)。 +2. 传入 JSX 元素里的属性类型的查找方式不同。 - 固有元素属性_本身_就支持,然而自定义的组件会自己去指定它们具有哪个属性。 + 固有元素属性*本身*就支持,然而自定义的组件会自己去指定它们具有哪个属性。 -TypeScript使用[与React相同的规范](http://facebook.github.io/react/docs/jsx-in-depth.html#html-tags-vs.-react-components) 来区别它们。 固有元素总是以一个小写字母开头,基于值的元素总是以一个大写字母开头。 +TypeScript 使用[与 React 相同的规范](http://facebook.github.io/react/docs/jsx-in-depth.html#html-tags-vs.-react-components) 来区别它们。 固有元素总是以一个小写字母开头,基于值的元素总是以一个大写字母开头。 ### 固有元素 @@ -58,9 +58,9 @@ TypeScript使用[与React相同的规范](http://facebook.github.io/react/docs/j ```typescript declare namespace JSX { - interface IntrinsicElements { - foo: any - } + interface IntrinsicElements { + foo: any; + } } ; // 正确 @@ -73,9 +73,9 @@ declare namespace JSX { ```typescript declare namespace JSX { - interface IntrinsicElements { - [elemName: string]: any; - } + interface IntrinsicElements { + [elemName: string]: any; + } } ``` @@ -84,7 +84,7 @@ declare namespace JSX { 基于值的元素会简单的在它所在的作用域里按标识符查找。 ```typescript -import MyComponent from "./myComponent"; +import MyComponent from './myComponent'; ; // 正确 ; // 错误 @@ -95,11 +95,11 @@ import MyComponent from "./myComponent"; 1. 函数组件 \(FC\) 2. 类组件 -由于这两种基于值的元素在JSX表达式里无法区分,因此TypeScript首先会尝试将表达式做为函数组件进行解析。如果解析成功,那么TypeScript就完成了表达式到其声明的解析操作。如果按照函数组件解析失败,那么TypeScript会继续尝试以类组件的形式进行解析。如果依旧失败,那么将输出一个错误。 +由于这两种基于值的元素在 JSX 表达式里无法区分,因此 TypeScript 首先会尝试将表达式做为函数组件进行解析。如果解析成功,那么 TypeScript 就完成了表达式到其声明的解析操作。如果按照函数组件解析失败,那么 TypeScript 会继续尝试以类组件的形式进行解析。如果依旧失败,那么将输出一个错误。 #### 函数组件 -正如其名,组件被定义成JavaScript函数,它的第一个参数是`props`对象。 TypeScript会强制它的返回值可以赋值给`JSX.Element`。 +正如其名,组件被定义成 JavaScript 函数,它的第一个参数是`props`对象。 TypeScript 会强制它的返回值可以赋值给`JSX.Element`。 ```typescript interface FooProp { @@ -116,7 +116,7 @@ function ComponentFoo(prop: FooProp) { const Button = (prop: {value: string}, context: { color: string }) => @@ -207,7 +207,7 @@ eval("console.log(new Animal().isDangerous())"); ```html @@ -222,7 +222,7 @@ eval("console.log(new Animal().isDangerous())"); ```ts // Which of these is a value that should be preserved? tsc knows, but `ts.transpileModule`, // ts-loader, esbuild, etc. don't, so `isolatedModules` gives an error. -import { someFunc, BaseType } from "./some-module.js"; +import { someFunc, BaseType } from './some-module.js'; // ^^^^^^^^ // Error: 'BaseType' is a type and must be imported using a type-only import // when 'preserveValueImports' and 'isolatedModules' are both enabled. @@ -239,7 +239,7 @@ import { someFunc, BaseType } from "./some-module.js"; ```ts // Which of these is a value that should be preserved? tsc knows, but `ts.transpileModule`, // ts-loader, esbuild, etc. don't, so `isolatedModules` issues an error. -import { someFunc, BaseType } from "./some-module.js"; +import { someFunc, BaseType } from './some-module.js'; // ^^^^^^^^ // Error: 'BaseType' is a type and must be imported using a type-only import // when 'preserveValueImports' and 'isolatedModules' are both enabled. @@ -249,8 +249,8 @@ import { someFunc, BaseType } from "./some-module.js"; TypeScript 已经有类似的功能,即 `import type`: ```ts -import type { BaseType } from "./some-module.js"; -import { someFunc } from "./some-module.js"; +import type { BaseType } from './some-module.js'; +import { someFunc } from './some-module.js'; export class Thing implements BaseType { // ... @@ -261,19 +261,19 @@ export class Thing implements BaseType { 因此,TypeScript 4.5 允许在每个命名导入前使用 `type` 修饰符,你可以按需混合使用它们。 ```ts -import { someFunc, type BaseType } from "./some-module.js"; +import { someFunc, type BaseType } from './some-module.js'; export class Thing implements BaseType { - someMethod() { - someFunc(); - } + someMethod() { + someFunc(); + } } ``` 上例中,在 [`preserveValueImports`](/tsconfig#preserveValueImports) 模式下,能够确定 `BaseType` 可以被删除,同时 `someFunc` 应该被保留,于是就会生成如下代码: ```js -import { someFunc } from "./some-module.js"; +import { someFunc } from './some-module.js'; export class Thing { someMethod() { @@ -291,17 +291,19 @@ TypeScript 4.5 支持了检查对象上是否存在某私有字段的 ECMAScript ```ts class Person { - #name: string; - constructor(name: string) { - this.#name = name; - } - - equals(other: unknown) { - return other && - typeof other === "object" && - #name in other && // <- this is new! - this.#name === other.#name; - } + #name: string; + constructor(name: string) { + this.#name = name; + } + + equals(other: unknown) { + return ( + other && + typeof other === 'object' && + #name in other && // <- this is new! + this.#name === other.#name + ); + } } ``` @@ -313,11 +315,11 @@ class Person { ### 导入断言 -TypeScript 4.5 支持了 ECMAScript Proposal 中的 *导入断言*。 +TypeScript 4.5 支持了 ECMAScript Proposal 中的 _导入断言_。 该语法会被运行时所使用来检查导入是否为期望的格式。 ```ts -import obj from "./something.json" assert { type: "json" }; +import obj from './something.json' assert { type: 'json' }; ``` TypeScript 不会检查这些断言,因为它们依赖于宿主环境。 @@ -326,16 +328,14 @@ TypeScript 会保留原样,稍后让浏览器或者运行时来处理它们( ```ts // TypeScript 允许 // 但浏览器可能不允许 -import obj from "./something.json" assert { - type: "fluffy bunny" -}; +import obj from './something.json' assert { type: 'fluffy bunny' }; ``` 动态的 `import()` 调用可以通过第二个参数来使用导入断言。 ```ts -const obj = await import("./something.json", { - assert: { type: "json" }, +const obj = await import('./something.json', { + assert: { type: 'json' }, }); ``` @@ -375,7 +375,7 @@ TypeScript 通常会使用属性的类型来判断插入哪种初始化器,但 ![Hovering over a signature where `Buffer` isn't found, TypeScript replaces it with `any`.](https://devblogs.microsoft.com/typescript/wp-content/uploads/sites/11/2021/10/quick-info-unresolved-4-4.png) -上例中,没有找到 `Buffer`,因此 TypeScript 在 *quick info* 里显示了 `any`。 +上例中,没有找到 `Buffer`,因此 TypeScript 在 _quick info_ 里显示了 `any`。 在 TypeScript 4.5 中,TypeScript 会尽可能保留你编写的代码。 ![Hovering over a signature where `Buffer` isn't found, it continues to use the name `Buffer`.](https://devblogs.microsoft.com/typescript/wp-content/uploads/sites/11/2021/10/quick-info-unresolved-4-5.png) diff --git a/zh/release-notes/typescript-4.6.md b/zh/release-notes/typescript-4.6.md index 87f571d6..f06a4234 100644 --- a/zh/release-notes/typescript-4.6.md +++ b/zh/release-notes/typescript-4.6.md @@ -9,18 +9,18 @@ ```ts class Base { - // ... + // ... } class Derived extends Base { - someProperty = true; - - constructor() { - // 错误! - // 必须先调用 'super()' 因为需要初始化 'someProperty'。 - doSomeStuff(); - super(); - } + someProperty = true; + + constructor() { + // 错误! + // 必须先调用 'super()' 因为需要初始化 'someProperty'。 + doSomeStuff(); + super(); + } } ``` @@ -37,20 +37,19 @@ TypeScript 可以根据判别式属性来细化类型。 ```ts type Action = - | { kind: "NumberContents", payload: number } - | { kind: "StringContents", payload: string }; + | { kind: 'NumberContents'; payload: number } + | { kind: 'StringContents'; payload: string }; function processAction(action: Action) { - if (action.kind === "NumberContents") { - // `action.payload` is a number here. - let num = action.payload * 2 - // ... - } - else if (action.kind === "StringContents") { - // `action.payload` is a string here. - const str = action.payload.trim(); - // ... - } + if (action.kind === 'NumberContents') { + // `action.payload` is a number here. + let num = action.payload * 2; + // ... + } else if (action.kind === 'StringContents') { + // `action.payload` is a string here. + const str = action.payload.trim(); + // ... + } } ``` @@ -61,19 +60,18 @@ function processAction(action: Action) { ```ts type Action = - | { kind: "NumberContents", payload: number } - | { kind: "StringContents", payload: string }; + | { kind: 'NumberContents'; payload: number } + | { kind: 'StringContents'; payload: string }; function processAction(action: Action) { - const { kind, payload } = action; - if (kind === "NumberContents") { - let num = payload * 2 - // ... - } - else if (kind === "StringContents") { - const str = payload.trim(); - // ... - } + const { kind, payload } = action; + if (kind === 'NumberContents') { + let num = payload * 2; + // ... + } else if (kind === 'StringContents') { + const str = payload.trim(); + // ... + } } ``` @@ -95,19 +93,19 @@ TypeScript 要面对一些有趣的挑战,因为它是构建在结构化类型 ```ts interface Source { - prop: string; + prop: string; } interface Target { - prop: number; + prop: number; } function check(source: Source, target: Target) { - target = source; - // error! - // Type 'Source' is not assignable to type 'Target'. - // Types of property 'prop' are incompatible. - // Type 'string' is not assignable to type 'number'. + target = source; + // error! + // Type 'Source' is not assignable to type 'Target'. + // Types of property 'prop' are incompatible. + // Type 'string' is not assignable to type 'number'. } ``` @@ -119,15 +117,15 @@ function check(source: Source, target: Target) { ```ts interface Source { - prop: Source>; + prop: Source>; } interface Target { - prop: Target>; + prop: Target>; } function check(source: Source, target: Target) { - target = source; + target = source; } ``` @@ -143,7 +141,7 @@ TypeScript 使用了启发式的算法 - 当一个类型达到特定的检查深 ```ts interface Foo { - prop: T; + prop: T; } declare let x: Foo>>>>>; @@ -173,34 +171,34 @@ TypeScript 现在能够正确地推断通过索引访问到另一个映射对象 ```ts interface TypeMap { - "number": number; - "string": string; - "boolean": boolean; + number: number; + string: string; + boolean: boolean; } -type UnionRecord

= { [K in P]: - { - kind: K; - v: TypeMap[K]; - f: (p: TypeMap[K]) => void; - } +type UnionRecord

= { + [K in P]: { + kind: K; + v: TypeMap[K]; + f: (p: TypeMap[K]) => void; + }; }[P]; function processRecord(record: UnionRecord) { - record.f(record.v); + record.f(record.v); } // 这个调用之前是有问题的,但现在没有问题 processRecord({ - kind: "string", - v: "hello!", - - // 'val' 之前会隐式地获得类型 'string | number | boolean', - // 但现在会正确地推断为类型 'string'。 - f: val => { - console.log(val.toUpperCase()); - } -}) + kind: 'string', + v: 'hello!', + + // 'val' 之前会隐式地获得类型 'string | number | boolean', + // 但现在会正确地推断为类型 'string'。 + f: val => { + console.log(val.toUpperCase()); + }, +}); ``` 该模式已经被支持了并允许 TypeScript 判断 `record.f(record.v)` 调用是合理的, @@ -215,8 +213,8 @@ TypeScript 4.6 改进了这个情况,因此在启用 `processRecord` 时不再 函数签名可以声明为剩余参数且其类型可以为可辨识联合元组类型。 ```ts -function func(...args: ["str", string] | ["num", number]) { - // ... +function func(...args: ['str', string] | ['num', number]) { + // ... } ``` @@ -227,19 +225,19 @@ function func(...args: ["str", string] | ["num", number]) { 像这样 TypeScript 是由签名来推断函数类型时,TypeScript 能够根据依赖的参数来细化类型。 ```ts -type Func = (...args: ["a", number] | ["b", string]) => void; +type Func = (...args: ['a', number] | ['b', string]) => void; const f1: Func = (kind, payload) => { - if (kind === "a") { - payload.toFixed(); // 'payload' narrowed to 'number' - } - if (kind === "b") { - payload.toUpperCase(); // 'payload' narrowed to 'string' - } + if (kind === 'a') { + payload.toFixed(); // 'payload' narrowed to 'number' + } + if (kind === 'b') { + payload.toUpperCase(); // 'payload' narrowed to 'string' + } }; -f1("a", 42); -f1("b", "hello"); +f1('a', 42); +f1('b', 'hello'); ``` 更多详情请阅读 [PR](https://github.com/microsoft/TypeScript/pull/47190)。 @@ -265,8 +263,8 @@ export const el =

foo
; TypeScript 会生成如下的 JavaScript 代码: ```ts -import { jsx as _jsx } from "react/jsx-runtime"; -export const el = _jsx("div", { children: "foo" }, void 0); +import { jsx as _jsx } from 'react/jsx-runtime'; +export const el = _jsx('div', { children: 'foo' }, void 0); ``` 末尾的 `void 0` 参数是没用的,删掉它会减小打包的体积。 @@ -283,7 +281,7 @@ export const el = _jsx("div", { children: "foo" }, void 0); * @param y The second operand */ function add(x, y) { - return x + y; + return x + y; } ``` @@ -295,7 +293,7 @@ function add(x, y) { * @param y {number} The second operand */ function add(a, b) { - return a + b; + return a + b; } ``` @@ -334,11 +332,10 @@ const foo = 5678; ```js function container() { - export function foo() { -// ~~~~~~ -// error: Modifiers cannot appear here. - - } + export function foo() { + // ~~~~~~ + // error: Modifiers cannot appear here. + } } ``` diff --git a/zh/release-notes/typescript-4.7.md b/zh/release-notes/typescript-4.7.md index 8c988dfb..0d998084 100644 --- a/zh/release-notes/typescript-4.7.md +++ b/zh/release-notes/typescript-4.7.md @@ -13,9 +13,9 @@ TypeScript 4.7 正式地支持了该功能,它添加了两个新的 `module` ```json { - "compilerOptions": { - "module": "node16", - } + "compilerOptions": { + "module": "node16" + } } ``` @@ -28,12 +28,11 @@ Node.js 在 [package.json 中支持了一个新的设置](https://nodejs.org/api ```json { - "name": "my-package", - "type": "module", + "name": "my-package", + "type": "module", - "//": "...", - "dependencies": { - } + "//": "...", + "dependencies": {} } ``` @@ -41,12 +40,12 @@ Node.js 在 [package.json 中支持了一个新的设置](https://nodejs.org/api 若没有设置,则默认值为 CommonJS。 当一个文件被当做 ESM 模块进行解析时,会使用如下与 CommonJS 模块不同的规则: -* 允许使用 `import` / `export` 语句 -* 允许使用顶层的 `await` -* 相对路径导入必须提供完整的扩展名(需要使用 `import "./foo.js"` 而非 `import "./foo"`) -* 解析 `node_modules` 里的依赖可能不同 -* 不允许直接使用像 `require` 和 `module` 这样的全局值 -* 需要使用特殊的规则来导入 CommonJS 模块 +- 允许使用 `import` / `export` 语句 +- 允许使用顶层的 `await` +- 相对路径导入必须提供完整的扩展名(需要使用 `import "./foo.js"` 而非 `import "./foo"`) +- 解析 `node_modules` 里的依赖可能不同 +- 不允许直接使用像 `require` 和 `module` 这样的全局值 +- 需要使用特殊的规则来导入 CommonJS 模块 我们回头会介绍其中一部分。 @@ -54,8 +53,8 @@ Node.js 在 [package.json 中支持了一个新的设置](https://nodejs.org/api 当 TypeScript 遇到 `.ts`,`.tsx`,`.js` 或 `.jsx` 文件时, 它会向上查找 `package.json` 来确定该文件是否使用了 ESM,然后再以此决定: -* 如何查找该文件所导入的其它模块 -* 当需要产生输出的时,如何转换该文件 +- 如何查找该文件所导入的其它模块 +- 当需要产生输出的时,如何转换该文件 当一个 `.ts` 文件被编译为 ESM 时,ECMAScript `import` / `export` 语句在生成的 `.js` 文件中原样输出; 当一个 `.ts` 文件被编译为 CommonJS 模块时,则会产生与使用了 `--module commonjs` 选项一致的输出结果。 @@ -66,11 +65,11 @@ Node.js 在 [package.json 中支持了一个新的设置](https://nodejs.org/api ```ts // ./foo.ts export function helper() { - // ... + // ... } // ./bar.ts -import { helper } from "./foo"; // only works in CJS +import { helper } from './foo'; // only works in CJS helper(); ``` @@ -80,7 +79,7 @@ helper(); ```ts // ./bar.ts -import { helper } from "./foo.js"; // works in ESM & CJS +import { helper } from './foo.js'; // works in ESM & CJS helper(); ``` @@ -114,11 +113,11 @@ Node.js 允许 ESM 导入 CommonJS 模块,就如同它们是带有默认导出 ```ts // ./foo.cts export function helper() { - console.log("hello world!"); + console.log('hello world!'); } // ./bar.mts -import foo from "./foo.cjs"; +import foo from './foo.cjs'; // prints "hello world!" foo.helper(); @@ -131,11 +130,11 @@ foo.helper(); ```ts // ./foo.cts export function helper() { - console.log("hello world!"); + console.log('hello world!'); } // ./bar.mts -import { helper } from "./foo.cjs"; +import { helper } from './foo.cjs'; // prints "hello world!" helper(); @@ -146,7 +145,7 @@ helper(); 关于互操作性,TypeScript 特有的注意点是如下的语法: ```ts -import foo = require("foo"); +import foo = require('foo'); ``` 在 CommonJS 模块中,它可以归结为 `require()` 调用, @@ -157,13 +156,13 @@ import foo = require("foo"); ```ts // ./foo.cts export function helper() { - console.log("hello world!"); + console.log('hello world!'); } // ./bar.mts -import foo = require("./foo.cjs"); +import foo = require('./foo.cjs'); -foo.helper() +foo.helper(); ``` 最后值得注意的是在 CommonJS 模块里导入 ESM 的唯一方法是使用动态 `import()` 调用。 @@ -181,20 +180,20 @@ Node.js 在 `package.json` 支持了一个新的字段 [`exports`](https://nodej ```json // package.json { - "name": "my-package", - "type": "module", - "exports": { - ".": { - // Entry-point for `import "my-package"` in ESM - "import": "./esm/index.js", - - // Entry-point for `require("my-package") in CJS - "require": "./commonjs/index.cjs", - }, - }, - - // CJS fall-back for older versions of Node.js - "main": "./commonjs/index.cjs", + "name": "my-package", + "type": "module", + "exports": { + ".": { + // Entry-point for `import "my-package"` in ESM + "import": "./esm/index.js", + + // Entry-point for `require("my-package") in CJS + "require": "./commonjs/index.cjs" + } + }, + + // CJS fall-back for older versions of Node.js + "main": "./commonjs/index.cjs" } ``` @@ -216,34 +215,34 @@ TypeScript 会查找名为 `./lib/index.d.ts` 的文件。 ```json // package.json { - "name": "my-package", - "type": "module", - "exports": { - ".": { - // Entry-point for `import "my-package"` in ESM - "import": { - // Where TypeScript will look. - "types": "./types/esm/index.d.ts", - - // Where Node.js will look. - "default": "./esm/index.js" - }, - // Entry-point for `require("my-package") in CJS - "require": { - // Where TypeScript will look. - "types": "./types/commonjs/index.d.cts", - - // Where Node.js will look. - "default": "./commonjs/index.cjs" - }, - } - }, - - // Fall-back for older versions of TypeScript - "types": "./types/index.d.ts", - - // CJS fall-back for older versions of Node.js - "main": "./commonjs/index.cjs" + "name": "my-package", + "type": "module", + "exports": { + ".": { + // Entry-point for `import "my-package"` in ESM + "import": { + // Where TypeScript will look. + "types": "./types/esm/index.d.ts", + + // Where Node.js will look. + "default": "./esm/index.js" + }, + // Entry-point for `require("my-package") in CJS + "require": { + // Where TypeScript will look. + "types": "./types/commonjs/index.d.cts", + + // Where Node.js will look. + "default": "./commonjs/index.cjs" + } + } + }, + + // Fall-back for older versions of TypeScript + "types": "./types/index.d.ts", + + // CJS fall-back for older versions of Node.js + "main": "./commonjs/index.cjs" } ``` @@ -274,8 +273,8 @@ TypeScript 的规则则是如果一个文件里存在 `import` 或 `export` 语 在 `"auto"` 模式下,TypeScript 不但会检测 `import` 和 `export` 语句,它还会检测: -* 若启用了 `--module nodenext` / `--module node16`,那么 `package.json` 里的 `"type"` 字段是否为 `"module"`,以及 -* 若启用了 `--jsx react-jsx`,那么当前文件是否为 JSX 文件。 +- 若启用了 `--module nodenext` / `--module node16`,那么 `package.json` 里的 `"type"` 字段是否为 `"module"`,以及 +- 若启用了 `--jsx react-jsx`,那么当前文件是否为 JSX 文件。 在这些情况下,我们想将每个文件都当作模块文件。 @@ -293,14 +292,14 @@ TypeScript 的规则则是如果一个文件里存在 `import` 或 `export` 语 ```ts const key = Symbol(); -const numberOrString = Math.random() < 0.5 ? 42 : "hello"; +const numberOrString = Math.random() < 0.5 ? 42 : 'hello'; const obj = { - [key]: numberOrString, + [key]: numberOrString, }; -if (typeof obj[key] === "string") { - let str = obj[key].toUpperCase(); +if (typeof obj[key] === 'string') { + let str = obj[key].toUpperCase(); } ``` @@ -316,15 +315,15 @@ TypeScript 4.7 能够知道 `obj[key]` 的类型为 `string`。 const key = Symbol(); class C { - [key]: string; + [key]: string; - constructor(str: string) { - // oops, forgot to set 'this[key]' - } + constructor(str: string) { + // oops, forgot to set 'this[key]' + } - screamString() { - return this[key].toUpperCase(); - } + screamString() { + return this[key].toUpperCase(); + } } ``` @@ -339,38 +338,42 @@ TypeScript 4.7 可以对数组和对象里的函数进行更精细的类型推 ```ts declare function f(arg: { - produce: (n: string) => T, - consume: (x: T) => void } -): void; + produce: (n: string) => T; + consume: (x: T) => void; +}): void; // Works f({ - produce: () => "hello", - consume: x => x.toLowerCase() + produce: () => 'hello', + consume: x => x.toLowerCase(), }); // Works f({ - produce: (n: string) => n, - consume: x => x.toLowerCase(), + produce: (n: string) => n, + consume: x => x.toLowerCase(), }); // Was an error, now works. f({ - produce: n => n, - consume: x => x.toLowerCase(), + produce: n => n, + consume: x => x.toLowerCase(), }); // Was an error, now works. f({ - produce: function () { return "hello"; }, - consume: x => x.toLowerCase(), + produce: function () { + return 'hello'; + }, + consume: x => x.toLowerCase(), }); // Was an error, now works. f({ - produce() { return "hello" }, - consume: x => x.toLowerCase(), + produce() { + return 'hello'; + }, + consume: x => x.toLowerCase(), }); ``` @@ -387,11 +390,11 @@ TypeScript 现在会收集与泛型参数 `T` 的类型推断相关的函数, ```ts interface Box { - value: T; + value: T; } function makeBox(value: T) { - return { value }; + return { value }; } ``` @@ -400,7 +403,7 @@ function makeBox(value: T) { ```ts function makeHammerBox(hammer: Hammer) { - return makeBox(hammer); + return makeBox(hammer); } // 或者 @@ -451,19 +454,20 @@ const errorMap = new ErrorMap(); 例如,编写一个有条件类型,它返回元组类型的第一个元素如果它类似 `string` 类型的话。 ```ts -type FirstIfString = - T extends [infer S, ...unknown[]] - ? S extends string ? S : never - : never; +type FirstIfString = T extends [infer S, ...unknown[]] + ? S extends string + ? S + : never + : never; - // string +// string type A = FirstIfString<[string, number, number]>; // "hello" -type B = FirstIfString<["hello", number, number]>; +type B = FirstIfString<['hello', number, number]>; // "hello" | "world" -type C = FirstIfString<["hello" | "world", boolean]>; +type C = FirstIfString<['hello' | 'world', boolean]>; // never type D = FirstIfString<[boolean, number, string]>; @@ -476,11 +480,10 @@ type D = FirstIfString<[boolean, number, string]>; 我们也可以这样定义 `FirstIfString`: ```ts -type FirstIfString = - T extends [string, ...unknown[]] - // Grab the first type out of `T` - ? T[0] - : never; +type FirstIfString = T extends [string, ...unknown[]] + ? // Grab the first type out of `T` + T[0] + : never; ``` 它可以工作但要更多的“手动”操作且不够形象。 @@ -491,10 +494,9 @@ type FirstIfString = 为了省去那一层嵌套,TypeScript 4.7 允许在 `infer` 上应用约束。 ```ts -type FirstIfString = - T extends [infer S extends string, ...unknown[]] - ? S - : never; +type FirstIfString = T extends [infer S extends string, ...unknown[]] + ? S + : never; ``` 通过这种方式,在 TypeScript 去匹配 `S` 时,它也会保证 `S` 是 `string` 类型。 @@ -508,11 +510,11 @@ type FirstIfString = ```ts interface Animal { - animalStuff: any; + animalStuff: any; } interface Dog extends Animal { - dogStuff: any; + dogStuff: any; } // ... @@ -551,8 +553,8 @@ type Setter = (value: T) => void; ```ts interface State { - get: () => T; - set: (value: T) => void; + get: () => T; + set: (value: T) => void; } ``` @@ -572,15 +574,15 @@ interface State { ```ts interface State { - // ~~~~~ - // error! - // Type 'State' is not assignable to type 'State' as implied by variance annotation. - // Types of property 'set' are incompatible. - // Type '(value: sub-T) => void' is not assignable to type '(value: super-T) => void'. - // Types of parameters 'value' and 'value' are incompatible. - // Type 'super-T' is not assignable to type 'sub-T'. - get: () => T; - set: (value: T) => void; + // ~~~~~ + // error! + // Type 'State' is not assignable to type 'State' as implied by variance annotation. + // Types of property 'set' are incompatible. + // Type '(value: sub-T) => void' is not assignable to type '(value: super-T) => void'. + // Types of parameters 'value' and 'value' are incompatible. + // Type 'super-T' is not assignable to type 'sub-T'. + get: () => T; + set: (value: T) => void; } ``` @@ -593,21 +595,21 @@ TypeScript 已经在尝试推断类型参数的变型并做为一项优化。 ```ts type Foo = { - x: T; - f: Bar; -} + x: T; + f: Bar; +}; type Bar = (x: Baz) => void; type Baz = { - value: Foo; -} + value: Foo; +}; declare let foo1: Foo; declare let foo2: Foo; -foo1 = foo2; // Should be an error but isn't ❌ -foo2 = foo1; // Error - correct ✅ +foo1 = foo2; // Should be an error but isn't ❌ +foo2 = foo1; // Error - correct ✅ ``` 提供明确的类型注解能够加快对环状类型的解析速度,有利于提高准确度。 @@ -647,7 +649,7 @@ TypeScript 4.7 支持了 `moduleSuffixes` 选项来自定义模块说明符的 对于上述配置,如果有如下的导入语句: ```ts -import * as foo from "./foo"; +import * as foo from './foo'; ``` 它会尝试查找文件 `./foo.ios.ts`,`./foo.native.ts` 最后是 `./foo.ts`。 @@ -678,14 +680,10 @@ TypeScript 允许使用 `/// ` 指令。 ```ts // Resolve `pkg` as if we were importing with a `require()` -import type { TypeFromRequire } from "pkg" assert { - "resolution-mode": "require" -}; +import type { TypeFromRequire } from 'pkg' assert { 'resolution-mode': 'require' }; // Resolve `pkg` as if we were importing with an `import` -import type { TypeFromImport } from "pkg" assert { - "resolution-mode": "import" -}; +import type { TypeFromImport } from 'pkg' assert { 'resolution-mode': 'import' }; export interface MergedType extends TypeFromRequire, TypeFromImport {} ``` @@ -693,11 +691,9 @@ export interface MergedType extends TypeFromRequire, TypeFromImport {} 这些断言也可以用在 `import()` 类型上。 ```ts -export type TypeFromRequire = - import("pkg", { assert: { "resolution-mode": "require" } }).TypeFromRequire; +export type TypeFromRequire = import('pkg').TypeFromRequire; -export type TypeFromImport = - import("pkg", { assert: { "resolution-mode": "import" } }).TypeFromImport; +export type TypeFromImport = import('pkg').TypeFromImport; export interface MergedType extends TypeFromRequire, TypeFromImport {} ``` @@ -717,8 +713,8 @@ Try updating with 'npm install -D typescript@next'. ## 跳转到在源码中的定义 -TypeScript 4.7 支持了一个实验性的编辑器功能叫作 *Go To Source Definition* (跳转到在源码中的定义)。 -它和 *Go To Definition* (跳转到定义)相似,但不是跳转到声明文件中。 +TypeScript 4.7 支持了一个实验性的编辑器功能叫作 _Go To Source Definition_ (跳转到在源码中的定义)。 +它和 _Go To Definition_ (跳转到定义)相似,但不是跳转到声明文件中。 而是查找相应的*实现*文件(比如 `.js` 或 `.ts` 文件),并且在那里查找定义 - 即便这些文件总是会被声明文件 `.d.ts` 所遮蔽。 @@ -741,14 +737,14 @@ TypeScript 为 JavaScript 和 TypeScript 提供了叫做 “Organize Imports” ```ts // local code -import * as bbb from "./bbb"; -import * as ccc from "./ccc"; -import * as aaa from "./aaa"; +import * as bbb from './bbb'; +import * as ccc from './ccc'; +import * as aaa from './aaa'; // built-ins -import * as path from "path"; -import * as child_process from "child_process" -import * as fs from "fs"; +import * as path from 'path'; +import * as child_process from 'child_process'; +import * as fs from 'fs'; // some code... ``` @@ -757,14 +753,13 @@ import * as fs from "fs"; ```ts // local code -import * as child_process from "child_process"; -import * as fs from "fs"; +import * as child_process from 'child_process'; +import * as fs from 'fs'; // built-ins -import * as path from "path"; -import * as aaa from "./aaa"; -import * as bbb from "./bbb"; -import * as ccc from "./ccc"; - +import * as path from 'path'; +import * as aaa from './aaa'; +import * as bbb from './bbb'; +import * as ccc from './ccc'; // some code... ``` @@ -778,14 +773,14 @@ TypeScript 4.7 在 “Organize Imports” 时会考虑分组。 ```ts // local code -import * as aaa from "./aaa"; -import * as bbb from "./bbb"; -import * as ccc from "./ccc"; +import * as aaa from './aaa'; +import * as bbb from './bbb'; +import * as ccc from './ccc'; // built-ins -import * as child_process from "child_process"; -import * as fs from "fs"; -import * as path from "path"; +import * as child_process from 'child_process'; +import * as fs from 'fs'; +import * as path from 'path'; // some code... ``` diff --git a/zh/release-notes/typescript-4.8.md b/zh/release-notes/typescript-4.8.md index 30a7def1..256539ba 100644 --- a/zh/release-notes/typescript-4.8.md +++ b/zh/release-notes/typescript-4.8.md @@ -13,8 +13,8 @@ TypeScript 现在能够识别出这种情况,允许将 `unknown` 赋值给 `{} ```ts function f(x: unknown, y: {} | null | undefined) { - x = y; // 可以工作 - y = x; // 以前会报错,现在可以工作 + x = y; // 可以工作 + y = x; // 以前会报错,现在可以工作 } ``` @@ -33,8 +33,8 @@ function f(x: unknown, y: {} | null | undefined) { ```ts function foo(x: NonNullable, y: NonNullable>) { - x = y; // 一直没问题 - y = x; // 以前会报错,现在没问题 + x = y; // 一直没问题 + y = x; // 以前会报错,现在没问题 } ``` @@ -43,21 +43,19 @@ function foo(x: NonNullable, y: NonNullable>) { ```ts function narrowUnknownishUnion(x: {} | null | undefined) { - if (x) { - x; // {} - } - else { - x; // {} | null | undefined - } + if (x) { + x; // {} + } else { + x; // {} | null | undefined + } } function narrowUnknown(x: unknown) { - if (x) { - x; // 以前是 'unknown',现在是 '{}' - } - else { - x; // unknown - } + if (x) { + x; // 以前是 'unknown',现在是 '{}' + } else { + x; // unknown + } } ``` @@ -68,13 +66,13 @@ TypeScript 会将其与 `{}` 进行交叉 - 等同于使用 `NonNullable`。 ```ts function throwIfNullable(value: T): NonNullable { - if (value === undefined || value === null) { - throw Error("Nullable value!"); - } + if (value === undefined || value === null) { + throw Error('Nullable value!'); + } - // 以前会报错,因为 'T' 不能赋值给 'NonNullable'。 - // 现在会细化为 'T & {}' 并且不报错,因为它等同于 'NonNullable'。 - return value; + // 以前会报错,因为 'T' 不能赋值给 'NonNullable'。 + // 现在会细化为 'T & {}' 并且不报错,因为它等同于 'NonNullable'。 + return value; } ``` @@ -92,21 +90,22 @@ function throwIfNullable(value: T): NonNullable { ```ts // 提取元组类型中的第一个元素,若其能够赋值给 'number', // 返回 'never' 若无这样的元素。 -type TryGetNumberIfFirst = - T extends [infer U extends number, ...unknown[]] ? U : never; +type TryGetNumberIfFirst = T extends [infer U extends number, ...unknown[]] + ? U + : never; ``` 若 `infer` 类型出现在模版字符串类型中且被原始类型所约束,则 TypeScript 会尝试将其解析为字面量类型。 ```ts // SomeNum 以前是 'number';现在是 '100'。 -type SomeNum = "100" extends `${infer U extends number}` ? U : never; +type SomeNum = '100' extends `${infer U extends number}` ? U : never; // SomeBigInt 以前是 'bigint';现在是 '100n'。 -type SomeBigInt = "100" extends `${infer U extends bigint}` ? U : never; +type SomeBigInt = '100' extends `${infer U extends bigint}` ? U : never; // SomeBool 以前是 'boolean';现在是 'true'。 -type SomeBool = "true" extends `${infer U extends boolean}` ? U : never; +type SomeBool = 'true' extends `${infer U extends boolean}` ? U : never; ``` 现在它能更好地表达代码库在运行时的行为,提供更准确的类型。 @@ -118,7 +117,7 @@ type SomeBool = "true" extends `${infer U extends boolean}` ? U : never; ```ts // JustNumber 为 `number` 因为 TypeScript 解析 出 `"1.0"`,但 `String(Number("1.0"))` 为 `"1"` 不匹配。 -type JustNumber = "1.0" extends `${infer T extends number}` ? T : never; +type JustNumber = '1.0' extends `${infer T extends number}` ? T : never; ``` 更多详情请参考[这里](https://github.com/microsoft/TypeScript/pull/48094)。 @@ -153,9 +152,9 @@ if people_at_home == []: let peopleAtHome = []; if (peopleAtHome === []) { -// ~~~~~~~~~~~~~~~~~~~ -// This condition will always return 'false' since JavaScript compares objects by reference, not value. - console.log("that's where she lies, broken inside. (x: T, y: T): T; -let [a, b, c] = chooseRandomly([42, true, "hi!"], [0, false, "bye!"]); +let [a, b, c] = chooseRandomly([42, true, 'hi!'], [0, false, 'bye!']); // ^ ^ ^ // | | | // | | string @@ -240,10 +239,10 @@ TypeScript 4.8 增加了一个编辑器首选项从自动导入中排除指定 ```json { - // Note that `javascript.preferences.autoImportFileExcludePatterns` can be specified for JavaScript too. - "typescript.preferences.autoImportFileExcludePatterns": [ - "**/node_modules/@types/node" - ] + // Note that `javascript.preferences.autoImportFileExcludePatterns` can be specified for JavaScript too. + "typescript.preferences.autoImportFileExcludePatterns": [ + "**/node_modules/@types/node" + ] } ``` diff --git a/zh/release-notes/typescript-4.9.md b/zh/release-notes/typescript-4.9.md index 9f088c9a..310937d5 100644 --- a/zh/release-notes/typescript-4.9.md +++ b/zh/release-notes/typescript-4.9.md @@ -9,10 +9,10 @@ TypeScript 开发者有时会感到进退两难:既想要确保表达式能够 ```ts // 每个属性可能是 string 或 RGB 元组。 const palette = { - red: [255, 0, 0], - green: "#00ff00", - bleu: [0, 0, 255] -// ^^^^ 拼写错误 + red: [255, 0, 0], + green: '#00ff00', + bleu: [0, 0, 255], + // ^^^^ 拼写错误 }; // 我们想要在 'red' 上调用数组的方法 @@ -27,15 +27,15 @@ const greenNormalized = palette.green.toUpperCase(); 但同时我们也失去了属性各自的信息。 ```ts -type Colors = "red" | "green" | "blue"; +type Colors = 'red' | 'green' | 'blue'; type RGB = [red: number, green: number, blue: number]; const palette: Record = { - red: [255, 0, 0], - green: "#00ff00", - bleu: [0, 0, 255] -// ~~~~ 能够检测到拼写错误 + red: [255, 0, 0], + green: '#00ff00', + bleu: [0, 0, 255], + // ~~~~ 能够检测到拼写错误 }; // 意想不到的错误 - 'palette.red' 可能为 string @@ -46,15 +46,15 @@ const redComponent = palette.red.at(0); 例如,可以使用 `satisfies` 来检验 `palette` 的所有属性与 `string | number[]` 是否兼容: ```ts -type Colors = "red" | "green" | "blue"; +type Colors = 'red' | 'green' | 'blue'; type RGB = [red: number, green: number, blue: number]; const palette = { - red: [255, 0, 0], - green: "#00ff00", - bleu: [0, 0, 255] -// ~~~~ 捕获拼写错误 + red: [255, 0, 0], + green: '#00ff00', + bleu: [0, 0, 255], + // ~~~~ 捕获拼写错误 } satisfies Record; // 依然可以访问这些方法 @@ -66,15 +66,15 @@ const greenNormalized = palette.green.toUpperCase(); 例如,检查一个对象是否包含了某个类型要求的所有的键,并且没有多余的: ```ts -type Colors = "red" | "green" | "blue"; +type Colors = 'red' | 'green' | 'blue'; // 确保仅包含 'Colors' 中定义的键 const favoriteColors = { - "red": "yes", - "green": false, - "blue": "kinda", - "platypus": false -// ~~~~~~~~~~ 错误 - "platypus" 不在 'Colors' 中 + red: 'yes', + green: false, + blue: 'kinda', + platypus: false, + // ~~~~~~~~~~ 错误 - "platypus" 不在 'Colors' 中 } satisfies Record; // 'red', 'green', and 'blue' 的类型信息保留下来 @@ -88,10 +88,10 @@ const g: boolean = favoriteColors.green; type RGB = [red: number, green: number, blue: number]; const palette = { - red: [255, 0, 0], - green: "#00ff00", - blue: [0, 0] - // ~~~~~~ 错误! + red: [255, 0, 0], + green: '#00ff00', + blue: [0, 0], + // ~~~~~~ 错误! } satisfies Record; // 类型信息保留下来 @@ -112,22 +112,22 @@ JavaScript 的 `in` 运算符能够检查对象上是否存在某个属性。 ```ts interface RGB { - red: number; - green: number; - blue: number; + red: number; + green: number; + blue: number; } interface HSV { - hue: number; - saturation: number; - value: number; + hue: number; + saturation: number; + value: number; } function setColor(color: RGB | HSV) { - if ("hue" in color) { - // 'color' 类型为 HSV - } - // ... + if ('hue' in color) { + // 'color' 类型为 HSV + } + // ... } ``` @@ -139,16 +139,16 @@ function setColor(color: RGB | HSV) { ```ts function tryGetPackageName(context) { - const packageJSON = context.packageJSON; - // Check to see if we have an object. - if (packageJSON && typeof packageJSON === "object") { - // Check to see if it has a string name property. - if ("name" in packageJSON && typeof packageJSON.name === "string") { - return packageJSON.name; - } + const packageJSON = context.packageJSON; + // Check to see if we have an object. + if (packageJSON && typeof packageJSON === 'object') { + // Check to see if it has a string name property. + if ('name' in packageJSON && typeof packageJSON.name === 'string') { + return packageJSON.name; } + } - return undefined; + return undefined; } ``` @@ -157,24 +157,24 @@ function tryGetPackageName(context) { ```ts interface Context { - packageJSON: unknown; + packageJSON: unknown; } function tryGetPackageName(context: Context) { - const packageJSON = context.packageJSON; - // Check to see if we have an object. - if (packageJSON && typeof packageJSON === "object") { - // Check to see if it has a string name property. - if ("name" in packageJSON && typeof packageJSON.name === "string") { - // ~~~~ - // error! Property 'name' does not exist on type 'object. - return packageJSON.name; - // ~~~~ - // error! Property 'name' does not exist on type 'object. - } + const packageJSON = context.packageJSON; + // Check to see if we have an object. + if (packageJSON && typeof packageJSON === 'object') { + // Check to see if it has a string name property. + if ('name' in packageJSON && typeof packageJSON.name === 'string') { + // ~~~~ + // error! Property 'name' does not exist on type 'object. + return packageJSON.name; + // ~~~~ + // error! Property 'name' does not exist on type 'object. } + } - return undefined; + return undefined; } ``` @@ -190,21 +190,21 @@ TypeScript 4.9 增强了 `in` 运算符的类型细化功能,它能够更好 ```ts interface Context { - packageJSON: unknown; + packageJSON: unknown; } function tryGetPackageName(context: Context): string | undefined { - const packageJSON = context.packageJSON; - // Check to see if we have an object. - if (packageJSON && typeof packageJSON === "object") { - // Check to see if it has a string name property. - if ("name" in packageJSON && typeof packageJSON.name === "string") { - // Just works! - return packageJSON.name; - } + const packageJSON = context.packageJSON; + // Check to see if we have an object. + if (packageJSON && typeof packageJSON === 'object') { + // Check to see if it has a string name property. + if ('name' in packageJSON && typeof packageJSON.name === 'string') { + // Just works! + return packageJSON.name; } + } - return undefined; + return undefined; } ``` @@ -220,11 +220,11 @@ TypeScript 4.9 支持了 ECMAScript 即将引入的“自动存取器”功能 ```ts class Person { - accessor name: string; + accessor name: string; - constructor(name: string) { - this.name = name; - } + constructor(name: string) { + this.name = name; + } } ``` @@ -232,18 +232,18 @@ class Person { ```ts class Person { - #__name: string; - - get name() { - return this.#__name; - } - set name(value: string) { - this.#__name = name; - } - - constructor(name: string) { - this.name = name; - } + #__name: string; + + get name() { + return this.#__name; + } + set name(value: string) { + this.#__name = name; + } + + constructor(name: string) { + this.name = name; + } } ``` @@ -257,36 +257,36 @@ class Person { 没有值等于 `NaN`,包括 `NaN` 自己! ```ts -console.log(NaN == 0) // false -console.log(NaN === 0) // false +console.log(NaN == 0); // false +console.log(NaN === 0); // false -console.log(NaN == NaN) // false -console.log(NaN === NaN) // false +console.log(NaN == NaN); // false +console.log(NaN === NaN); // false ``` 换句话说,任何值都不等于 `NaN`。 ```ts -console.log(NaN != 0) // true -console.log(NaN !== 0) // true +console.log(NaN != 0); // true +console.log(NaN !== 0); // true -console.log(NaN != NaN) // true -console.log(NaN !== NaN) // true +console.log(NaN != NaN); // true +console.log(NaN !== NaN); // true ``` 从技术上讲,这不是 JavaScript 独有的问题,任何使用 IEEE-754 浮点数的语言都有一样的问题; 但是 JavaScript 中主要的数值类型为浮点数,并且解析数值时经常会得到 `NaN`。 -因此,检查 `NaN` 是很常见的操作,正确的方法是使用 `Number.isNaN` 函数 - +因此,检查 `NaN` 是很常见的操作,正确的方法是使用 `Number.isNaN` 函数 - 但像上文提到的,很多人可能不小心地使用了 `someValue === NaN` 来进行检查。 现在,如果 TypeScript 发现直接比较 `NaN` 会报错,并提示使用 `Number.isNaN`。 ```ts function validate(someValue: number) { - return someValue !== NaN; - // ~~~~~~~~~~~~~~~~~ - // error: This condition will always return 'true'. - // Did you mean '!Number.isNaN(someValue)'? + return someValue !== NaN; + // ~~~~~~~~~~~~~~~~~ + // error: This condition will always return 'true'. + // Did you mean '!Number.isNaN(someValue)'? } ``` @@ -333,8 +333,8 @@ VS Code 支持了很多[远程开发](https://marketplace.visualstudio.com/searc 拿下面的代码举例: ```ts -import { Zebra, Moose, HoneyBadger } from "./zoo"; -import { foo, bar } from "./helper"; +import { Zebra, Moose, HoneyBadger } from './zoo'; +import { foo, bar } from './helper'; let x: Moose | HoneyBadger = foo(); ``` @@ -343,8 +343,8 @@ let x: Moose | HoneyBadger = foo(); 因此会将上面的代码重写为: ```ts -import { foo } from "./helper"; -import { HoneyBadger, Moose } from "./zoo"; +import { foo } from './helper'; +import { HoneyBadger, Moose } from './zoo'; let x: Moose | HoneyBadger = foo(); ``` @@ -352,8 +352,8 @@ let x: Moose | HoneyBadger = foo(); 在 TypeScript 4.3 中,引入了“排序导入语句”命令,它仅排序导入语句但不进行删除,因此会将上例代码重写为: ```ts -import { bar, foo } from "./helper"; -import { HoneyBadger, Moose, Zebra } from "./zoo"; +import { bar, foo } from './helper'; +import { HoneyBadger, Moose, Zebra } from './zoo'; let x: Moose | HoneyBadger = foo(); ``` @@ -364,8 +364,8 @@ TypeScript 4.9 添加了另一半功能,提供了“移除未使用的导入 TypeScript 会移除未使用的导入命名和语句,但是不能改变当前的排序。 ```ts -import { Moose, HoneyBadger } from "./zoo"; -import { foo } from "./helper"; +import { Moose, HoneyBadger } from './zoo'; +import { foo } from './helper'; let x: Moose | HoneyBadger = foo(); ``` @@ -403,7 +403,7 @@ TypeScript 进行了一些较小的但是能觉察到的性能优化。 ```ts interface Zoo { - // ... + // ... } type MakeZoo = A extends Animal ? Zoo : never; diff --git a/zh/release-notes/typescript-5.0.md b/zh/release-notes/typescript-5.0.md index d26c207b..af77b0e0 100644 --- a/zh/release-notes/typescript-5.0.md +++ b/zh/release-notes/typescript-5.0.md @@ -1235,9 +1235,9 @@ TypeScript 现在对一些常用的机制进行缓存,以便在这些操作之 更多详情: -* [Migrate to Modules](https://github.com/microsoft/TypeScript/pull/51387) -* [Node Monomorphization](https://github.com/microsoft/TypeScript/pull/51682) -* [Symbol Monomorphization](https://github.com/microsoft/TypeScript/pull/51880) -* [Identifier Size Reduction](https://github.com/microsoft/TypeScript/pull/52170) -* [Printer Caching](https://github.com/microsoft/TypeScript/pull/52382) -* [Limited Usage of var](https://github.com/microsoft/TypeScript/issues/52924) +- [Migrate to Modules](https://github.com/microsoft/TypeScript/pull/51387) +- [Node Monomorphization](https://github.com/microsoft/TypeScript/pull/51682) +- [Symbol Monomorphization](https://github.com/microsoft/TypeScript/pull/51880) +- [Identifier Size Reduction](https://github.com/microsoft/TypeScript/pull/52170) +- [Printer Caching](https://github.com/microsoft/TypeScript/pull/52382) +- [Limited Usage of var](https://github.com/microsoft/TypeScript/issues/52924) diff --git a/zh/release-notes/typescript-5.1.md b/zh/release-notes/typescript-5.1.md index e7597b51..82a3cf23 100644 --- a/zh/release-notes/typescript-5.1.md +++ b/zh/release-notes/typescript-5.1.md @@ -300,7 +300,7 @@ TypeScript 现在支持 _链接编辑_ JSX 标签名。 这个新特性在 TypeScript 和 JavaScript 里都可用,并且可以在 Visual Studio Code Insiders 版本中启用。 在 Visual Studio Code 里,你既可以用设置界面的 `Editor: Linked Editing` 配置: -![](https://devblogs.microsoft.com/typescript/wp-content/uploads/sites/11/2023/04/linkedEditing-5.1-vscode-ui-1.png) +![x](https://devblogs.microsoft.com/typescript/wp-content/uploads/sites/11/2023/04/linkedEditing-5.1-vscode-ui-1.png) 也可以用 JSON 配置文件中的 `editor.linkedEditing`: diff --git a/zh/release-notes/typescript-5.2.md b/zh/release-notes/typescript-5.2.md index e1a29b0b..3c88c946 100644 --- a/zh/release-notes/typescript-5.2.md +++ b/zh/release-notes/typescript-5.2.md @@ -559,12 +559,12 @@ Symbol.metadata ??= Symbol('Symbol.metadata'); 你还需要将编译 `target` 设为 `es2022` 或以下,配置 `lib` 为 `"esnext"` 或者 `"esnext.decorators"`。 -``` +```json { - "compilerOptions": { - "target": "es2022", - "lib": ["es2022", "esnext.decorators", "dom"] - } + "compilerOptions": { + "target": "es2022", + "lib": ["es2022", "esnext.decorators", "dom"] + } } ``` @@ -707,7 +707,9 @@ doSomething( const myWeakMap = new WeakMap(); const key = Symbol(); -const someObject = { /*...*/ }; +const someObject = { + /*...*/ +}; // Works! ✅ myWeakMap.set(key, someObject); @@ -723,10 +725,10 @@ TypeScript 支持在类型导入路径里使用声明文件扩展名和实现文 也意味着你现在可以编写 `import type` 语句并使用 `.ts`, `.mts`, `.cts` 以及 `.tsx` 文件扩展。 ```ts -import type { JustAType } from "./justTypes.ts"; +import type { JustAType } from './justTypes.ts'; export function f(param: JustAType) { - // ... + // ... } ``` @@ -737,7 +739,7 @@ export function f(param: JustAType) { * @param {import("./justTypes.ts").JustAType} param */ export function f(param) { - // ... + // ... } ``` @@ -777,13 +779,13 @@ TypeScript 5.2 现在具有一种重构方法,可以将变量的内容内联 ```ts interface A { - value: A; - other: string; + value: A; + other: string; } interface B { - value: B; - other: number; + value: B; + other: number; } ``` @@ -797,7 +799,7 @@ interface B { 在 TypeScript 5.2 中,一个简单的 `Set` 就能跟踪这些信息。 在使用了 drizzle 库的测试报告中,这项改动减少了超过 33% 的时间花费! -``` +```txt Benchmark 1: old Time (mean ± σ): 3.115 s ± 0.067 s [User: 4.403 s, System: 0.124 s] Range (min … max): 3.018 s … 3.196 s 10 runs diff --git a/zh/release-notes/typescript-5.4.md b/zh/release-notes/typescript-5.4.md index 945c2b84..7c44d4a7 100644 --- a/zh/release-notes/typescript-5.4.md +++ b/zh/release-notes/typescript-5.4.md @@ -312,9 +312,9 @@ import * as ns from "foo" with { type: "not-json" }; TypeScript 现在提供了一个快速修复选项,可以为被调用时传递了过多参数的函数添加一个新的参数。 -![](https://devblogs.microsoft.com/typescript/wp-content/uploads/sites/11/2024/01/add-missing-params-5-4-beta-before.png) +![x](https://devblogs.microsoft.com/typescript/wp-content/uploads/sites/11/2024/01/add-missing-params-5-4-beta-before.png) -![](https://devblogs.microsoft.com/typescript/wp-content/uploads/sites/11/2024/01/add-missing-params-5-4-beta-after.png) +![x](https://devblogs.microsoft.com/typescript/wp-content/uploads/sites/11/2024/01/add-missing-params-5-4-beta-after.png) 当在多个现有函数之间传递一个新参数时,这将非常有用,而目前这样做可能会很麻烦。 diff --git a/zh/tutorials/angular-2.md b/zh/tutorials/angular-2.md index 1002f8dc..d6887ef3 100644 --- a/zh/tutorials/angular-2.md +++ b/zh/tutorials/angular-2.md @@ -1,6 +1,5 @@ # Angular 2 -即将到来的Angular 2框架是使用TypeScript开发的。 因此Angular和TypeScript一起使用非常简单方便。 Angular团队也在其文档里把TypeScript视为一等公民。 - -正因为这样,你总是可以在[Angular 2官网](https://angular.io)\(或[Angular 2官网中文版](https://angular.cn)\)里查看到最新的结合使用Angular和TypeScript的参考文档。 在这里查看[快速上手指南](https://angular.io/docs/ts/latest/quickstart.html),现在就开始学习吧! +即将到来的 Angular 2 框架是使用 TypeScript 开发的。 因此 Angular 和 TypeScript 一起使用非常简单方便。 Angular 团队也在其文档里把 TypeScript 视为一等公民。 +正因为这样,你总是可以在[Angular 2 官网](https://angular.io)\(或[Angular 2 官网中文版](https://angular.cn)\)里查看到最新的结合使用 Angular 和 TypeScript 的参考文档。 在这里查看[快速上手指南](https://angular.io/docs/ts/latest/quickstart.html),现在就开始学习吧! diff --git a/zh/tutorials/asp.net-4.md b/zh/tutorials/asp.net-4.md index 9b0581f9..e221ede1 100644 --- a/zh/tutorials/asp.net-4.md +++ b/zh/tutorials/asp.net-4.md @@ -45,9 +45,11 @@ ```typescript function sayHello() { - const compiler = (document.getElementById("compiler") as HTMLInputElement).value; - const framework = (document.getElementById("framework") as HTMLInputElement).value; - return `Hello from ${compiler} and ${framework}!`; + const compiler = (document.getElementById('compiler') as HTMLInputElement) + .value; + const framework = (document.getElementById('framework') as HTMLInputElement) + .value; + return `Hello from ${compiler} and ${framework}!`; } ``` @@ -82,7 +84,7 @@ function sayHello() { 3. 显式列出了 `"files"` 而不是依据 `"excludes"`选项。 4. 设置 `"compileOnSave": true`。 -当你写新代码时,设置 `"noImplicitAny"` 选项是个好主意 — 这可以确保你不会错写任何新的类型。 设置 `"compileOnSave"` 选项可以确保你在运行web程序前自动编译保存变更后的代码。 更多信息请参见 [the tsconfig.json documentation](../project-config/tsconfig.json.md)。 +当你写新代码时,设置 `"noImplicitAny"` 选项是个好主意 — 这可以确保你不会错写任何新的类型。 设置 `"compileOnSave"` 选项可以确保你在运行 web 程序前自动编译保存变更后的代码。 更多信息请参见 [the tsconfig.json documentation](../project-config/tsconfig.json.md)。 ### 在视图中调用脚本 @@ -116,11 +118,11 @@ function sayHello() { 1. 在 Edge 浏览器中, 按 F12 键并选择 **Debugger** 标签页。 2. 展开 localhost 列表, 选择 src/app.ts 3. 在 `return` 那一行上打一个断点。 -4. 在输入框中键入一些内容,确认TypeScript代码命中断点,观察它是否能正确地工作。 +4. 在输入框中键入一些内容,确认 TypeScript 代码命中断点,观察它是否能正确地工作。 ![Demo paused on breakpoint](../assets/images/tutorials/aspnet/paused-demo.png) -这就是你需要知道的在ASP.NET中使用TypeScript的基本知识了。接下来,我们引入Angular,写一个简单的Angular程序示例。 +这就是你需要知道的在 ASP.NET 中使用 TypeScript 的基本知识了。接下来,我们引入 Angular,写一个简单的 Angular 程序示例。 ## 添加 Angular 2 @@ -129,7 +131,7 @@ function sayHello() { 1. 安装 [PackageInstaller](https://github.com/madskristensen/PackageInstaller)。 2. 用 PackageInstaller 来安装 Angular 2, systemjs 和 Typings。 - 在project上右击, 选择 **Quick Install Package**。 + 在 project 上右击, 选择 **Quick Install Package**。 ![Use PackageInstaller to install angular2](../assets/images/tutorials/aspnet/packageinstaller-angular2.png) ![Use PackageInstaller to install systemjs](../assets/images/tutorials/aspnet/packageinstaller-systemjs.png) ![Use PackageInstaller to install Typings](../assets/images/tutorials/aspnet/packageinstaller-typings.png) @@ -186,18 +188,18 @@ function sayHello() { 首先,将 `app.ts` 改成: ```typescript -import {Component} from "angular2/core" -import {MyModel} from "./model" +import { Component } from 'angular2/core'; +import { MyModel } from './model'; @Component({ - selector: `my-app`, - template: `
Hello from {{getCompiler()}}
` + selector: `my-app`, + template: `
Hello from {{ getCompiler() }}
`, }) class MyApp { - model = new MyModel(); - getCompiler() { - return this.model.compiler; - } + model = new MyModel(); + getCompiler() { + return this.model.compiler; + } } ``` @@ -205,15 +207,15 @@ class MyApp { ```typescript export class MyModel { - compiler = "TypeScript"; + compiler = 'TypeScript'; } ``` 再在 `src` 中添加 `main.ts`: ```typescript -import {bootstrap} from "angular2/platform/browser"; -import {MyApp} from "./app"; +import { bootstrap } from 'angular2/platform/browser'; +import { MyApp } from './app'; bootstrap(MyApp); ``` @@ -242,4 +244,3 @@ bootstrap(MyApp); ``` 这里加载了此应用。 运行 ASP.NET 应用,你应该能看到一个 div 显示 "Loading..." 紧接着更新成显示 "Hello from TypeScript"。 - diff --git a/zh/tutorials/asp.net-core.md b/zh/tutorials/asp.net-core.md index 81f88ec3..f5398013 100644 --- a/zh/tutorials/asp.net-core.md +++ b/zh/tutorials/asp.net-core.md @@ -4,22 +4,22 @@ ### 安装 ASP.NET Core 和 TypeScript -首先,若有需要请[安装 ASP.NET Core](https://get.asp.net)。此篇指南需要使用Visual Studio 2015或2017。 +首先,若有需要请[安装 ASP.NET Core](https://get.asp.net)。此篇指南需要使用 Visual Studio 2015 或 2017。 -其次,如果你的Visual Studio不带有最新版本的TypeScript,你可以从[这里](http://www.microsoft.com/en-us/download/details.aspx?id=48593)安装。 +其次,如果你的 Visual Studio 不带有最新版本的 TypeScript,你可以从[这里](http://www.microsoft.com/en-us/download/details.aspx?id=48593)安装。 ### 新建工程 1. 选择 **File** 2. 选择 **New Project** (Ctrl + Shift + N) 3. 选择 **Visual C\#** -4. 若使用VS2015,选择 **ASP.NET Web Application** > **ASP.NET 5 Empty**,并且取消勾选“Host in the cloud”,因为我们要在本地运行。 +4. 若使用 VS2015,选择 **ASP.NET Web Application** > **ASP.NET 5 Empty**,并且取消勾选“Host in the cloud”,因为我们要在本地运行。 - ![使用空白模版](../assets/images/tutorials/aspnet/new-asp-project-empty.png) + ![使用空白模版](../assets/images/tutorials/aspnet/new-asp-project-empty.png) -5. 若使用VS2017,选择 **ASP.NET Core Web Application (.NET Core)** > **ASP.NET Core 1.1 Empty**。 +5. 若使用 VS2017,选择 **ASP.NET Core Web Application (.NET Core)** > **ASP.NET Core 1.1 Empty**。 - ![使用空白模版VS2017](../assets/images/tutorials/aspnet/new-asp-project-empty-17.PNG) + ![使用空白模版VS2017](../assets/images/tutorials/aspnet/new-asp-project-empty-17.PNG) 运行此应用以确保它能正常工作。 @@ -58,7 +58,7 @@ public void Configure(IApplicationBuilder app) 打开 **Dependencies** > **Manage NuGet Packages** > **Browse**。搜索并安装`Microsoft.AspNetCore.StaticFiles` 1.1.2: -![安装Microsoft.AspNetCore.StaticFiles](../assets/images/tutorials/aspnet/install-nuget-packages.png) +![安装Microsoft.AspNetCore.StaticFiles](../assets/images/tutorials/aspnet/install-nuget-packages.png) 如下替换掉`Startup.cs`里`Configure`的内容: @@ -70,7 +70,7 @@ public void Configure(IApplicationBuilder app) } ``` -你可能需要重启VS,这样`UseDefaultFiles`和`UseStaticFiles`下面的波浪线才会消失。 +你可能需要重启 VS,这样`UseDefaultFiles`和`UseStaticFiles`下面的波浪线才会消失。 ## 添加 TypeScript @@ -90,12 +90,14 @@ public void Configure(IApplicationBuilder app) ### 添加示例代码 -将以下代码写入app.ts文件。 +将以下代码写入 app.ts 文件。 ```typescript function sayHello() { - const compiler = (document.getElementById("compiler") as HTMLInputElement).value; - const framework = (document.getElementById("framework") as HTMLInputElement).value; + const compiler = (document.getElementById('compiler') as HTMLInputElement) + .value; + const framework = (document.getElementById('framework') as HTMLInputElement) + .value; return `Hello from ${compiler} and ${framework}!`; } ``` @@ -104,7 +106,7 @@ function sayHello() { #### 配置 TypeScript 编译器 -我们先来告诉TypeScript怎样构建。 右击scripts文件夹并选择**New Item**。 接着选择**TypeScript Configuration File**,保持文件的默认名字为`tsconfig.json`。 +我们先来告诉 TypeScript 怎样构建。 右击 scripts 文件夹并选择**New Item**。 接着选择**TypeScript Configuration File**,保持文件的默认名字为`tsconfig.json`。 ![Create tsconfig.json](../assets/images/tutorials/aspnet/new-tsconfig.png) @@ -131,11 +133,11 @@ function sayHello() { 2. 显式列出了`"files"`而不是依据`"excludes"`。 3. 设置`"compileOnSave": true`。 -当你写新代码时,设置`"noImplicitAny"`选项是个不错的选择 — 这可以确保你不会错写任何新的类型。 设置`"compileOnSave"`选项可以确保你在运行web程序前自动编译保存变更后的代码。 +当你写新代码时,设置`"noImplicitAny"`选项是个不错的选择 — 这可以确保你不会错写任何新的类型。 设置`"compileOnSave"`选项可以确保你在运行 web 程序前自动编译保存变更后的代码。 #### 配置 NPM -现在,我们来配置NPM以使用我们能够下载JavaScript包。 在工程上右击并选择**New Item**。 接着选择**NPM Configuration File**,保持文件的默认名字为`package.json`。 在`"devDependencies"`部分添加"gulp"和"del": +现在,我们来配置 NPM 以使用我们能够下载 JavaScript 包。 在工程上右击并选择**New Item**。 接着选择**NPM Configuration File**,保持文件的默认名字为`package.json`。 在`"devDependencies"`部分添加"gulp"和"del": ```javascript "devDependencies": { @@ -144,11 +146,11 @@ function sayHello() { } ``` -保存这个文件后,Visual Studio将开始安装gulp和del。 若没有自动开始,请右击package.json文件选择**Restore Packages**。 +保存这个文件后,Visual Studio 将开始安装 gulp 和 del。 若没有自动开始,请右击 package.json 文件选择**Restore Packages**。 #### 设置 gulp -最后,添加一个新JavaScript文件`gulpfile.js`。 键入以下内容: +最后,添加一个新 JavaScript 文件`gulpfile.js`。 键入以下内容: ```javascript /// @@ -169,17 +171,17 @@ gulp.task('clean', function () { }); gulp.task('default', function () { - gulp.src(paths.scripts).pipe(gulp.dest('wwwroot/scripts')) + gulp.src(paths.scripts).pipe(gulp.dest('wwwroot/scripts')); }); ``` -第一行是告诉Visual Studio构建完成后,立即运行'default'任务。 当你应答 Visual Studio 清除构建内容后,它也将运行'clean'任务。 +第一行是告诉 Visual Studio 构建完成后,立即运行'default'任务。 当你应答 Visual Studio 清除构建内容后,它也将运行'clean'任务。 -现在,右击`gulpfile.js`并选择**Task Runner Explorer**。 若'default'和'clean'任务没有显示输出内容的话,请刷新explorer: +现在,右击`gulpfile.js`并选择**Task Runner Explorer**。 若'default'和'clean'任务没有显示输出内容的话,请刷新 explorer: ![Refresh Task Runner Explorer](../assets/images/tutorials/aspnet/task-runner-explorer.png) -### 编写HTML页 +### 编写 HTML 页 在`wwwroot`中添加一个新建项 `index.html`。 在`index.html`中写入以下代码: @@ -213,19 +215,19 @@ gulp.task('default', function () { 1. 在 Edge 浏览器中,按 F12 键并选择 **Debugger** 标签页。 2. 展开 localhost 列表,选择 scripts/app.ts 3. 在 `return` 那一行上打一个断点。 -4. 在输入框中键入一些内容,确认TypeScript代码命中断点,观察它是否能正确地工作。 +4. 在输入框中键入一些内容,确认 TypeScript 代码命中断点,观察它是否能正确地工作。 ![Demo paused on breakpoint](../assets/images/tutorials/aspnet/paused-demo.png) -这就是你需要知道的在ASP.NET中使用TypeScript的基本知识了。 接下来,我们引入Angular,写一个简单的Angular程序示例。 +这就是你需要知道的在 ASP.NET 中使用 TypeScript 的基本知识了。 接下来,我们引入 Angular,写一个简单的 Angular 程序示例。 ## 添加 Angular 2 ### 使用 NPM 下载依赖的包 -添加Angular 2和SystemJS到`package.json`的`dependencies`里。 +添加 Angular 2 和 SystemJS 到`package.json`的`dependencies`里。 -对于VS2015,新的`dependencies`列表如下: +对于 VS2015,新的`dependencies`列表如下: ```javascript "dependencies": { @@ -236,7 +238,7 @@ gulp.task('default', function () { }, ``` -若使用VS2017,因为NPM3反对同行的依赖(peer dependencies),我们需要把Angular 2同行的依赖也直接列为依赖项: +若使用 VS2017,因为 NPM3 反对同行的依赖(peer dependencies),我们需要把 Angular 2 同行的依赖也直接列为依赖项: ```javascript "dependencies": { @@ -252,9 +254,9 @@ gulp.task('default', function () { ### 更新 tsconfig.json -现在安装好了Angular 2及其依赖项,我们需要启用TypeScript中实验性的装饰器支持。 我们还需要添加ES2015的声明,因为Angular使用core-js来支持像`Promise`的功能。 在未来,装饰器会成为默认设置,那时也就不再需要这些设置了。 +现在安装好了 Angular 2 及其依赖项,我们需要启用 TypeScript 中实验性的装饰器支持。 我们还需要添加 ES2015 的声明,因为 Angular 使用 core-js 来支持像`Promise`的功能。 在未来,装饰器会成为默认设置,那时也就不再需要这些设置了。 -添加`"experimentalDecorators": true, "emitDecoratorMetadata": true`到`"compilerOptions"`部分。 然后,再添加`"lib": ["es2015", "es5", "dom"]`到`"compilerOptions"`,以引入ES2015的声明。 最后,我们需要添加`"./model.ts"`到`"files"`里,我们接下来会创建它。 现在`tsconfig.json`看起来如下: +添加`"experimentalDecorators": true, "emitDecoratorMetadata": true`到`"compilerOptions"`部分。 然后,再添加`"lib": ["es2015", "es5", "dom"]`到`"compilerOptions"`,以引入 ES2015 的声明。 最后,我们需要添加`"./model.ts"`到`"files"`里,我们接下来会创建它。 现在`tsconfig.json`看起来如下: ```javascript { @@ -319,41 +321,41 @@ gulp.task('default', ['lib'], function () { }); ``` -此外,保存了此gulpfile后,要确保 Task Runner Explorer 能看到 `lib` 任务。 +此外,保存了此 gulpfile 后,要确保 Task Runner Explorer 能看到 `lib` 任务。 ### 用 TypeScript 写一个简单的 Angular 应用 首先,将 `app.ts` 改成: ```typescript -import {Component} from "angular2/core" -import {MyModel} from "./model" +import { Component } from 'angular2/core'; +import { MyModel } from './model'; @Component({ - selector: `my-app`, - template: `
Hello from {{getCompiler()}}
` + selector: `my-app`, + template: `
Hello from {{ getCompiler() }}
`, }) export class MyApp { - model = new MyModel(); - getCompiler() { - return this.model.compiler; - } + model = new MyModel(); + getCompiler() { + return this.model.compiler; + } } ``` -接着在`scripts`中添加TypeScript文件`model.ts`: +接着在`scripts`中添加 TypeScript 文件`model.ts`: ```typescript export class MyModel { - compiler = "TypeScript"; + compiler = 'TypeScript'; } ``` 再在`scripts`中添加`main.ts`: ```typescript -import {bootstrap} from "angular2/platform/browser"; -import {MyApp} from "./app"; +import { bootstrap } from 'angular2/platform/browser'; +import { MyApp } from './app'; bootstrap(MyApp); ``` @@ -387,5 +389,4 @@ bootstrap(MyApp); ``` -这里加载了此应用。 运行 ASP.NET 应用,你应该能看到一个div显示"Loading..."紧接着更新成显示"Hello from TypeScript"。 - +这里加载了此应用。 运行 ASP.NET 应用,你应该能看到一个 div 显示"Loading..."紧接着更新成显示"Hello from TypeScript"。 diff --git a/zh/tutorials/gulp.md b/zh/tutorials/gulp.md index b476f465..29489206 100644 --- a/zh/tutorials/gulp.md +++ b/zh/tutorials/gulp.md @@ -1,6 +1,6 @@ # Gulp -这篇快速上手指南将教你如何使用[Gulp](http://gulpjs.com)构建TypeScript,和如何在Gulp管道里添加[Browserify](http://browserify.org),[uglify](http://lisperator.net/uglifyjs/)或[Watchify](https://github.com/substack/watchify)。 本指南还会展示如何使用[Babelify](https://github.com/babel/babelify)来添加[Babel](https://babeljs.io/)的功能。 +这篇快速上手指南将教你如何使用[Gulp](http://gulpjs.com)构建 TypeScript,和如何在 Gulp 管道里添加[Browserify](http://browserify.org),[uglify](http://lisperator.net/uglifyjs/)或[Watchify](https://github.com/substack/watchify)。 本指南还会展示如何使用[Babelify](https://github.com/babel/babelify)来添加[Babel](https://babeljs.io/)的功能。 这里假设你已经在使用[Node.js](https://nodejs.org/)和[npm](https://www.npmjs.com/)了。 @@ -21,7 +21,7 @@ proj/ └─ dist/ ``` -TypeScript文件放在`src`文件夹下,经过TypeScript编译器编译生成的目标文件放在`dist`目录下。 +TypeScript 文件放在`src`文件夹下,经过 TypeScript 编译器编译生成的目标文件放在`dist`目录下。 下面让我们来创建这些文件夹: @@ -32,7 +32,7 @@ mkdir dist ### 初始化工程 -现在让我们把这个文件夹转换成npm包: +现在让我们把这个文件夹转换成 npm 包: ```text npm init @@ -42,13 +42,13 @@ npm init ### 安装依赖项 -现在我们可以使用`npm install`命令来安装包。 首先全局安装`gulp-cli`(如果你使用Unix系统,你可能需要在`npm install`命令上使用`sudo`)。 +现在我们可以使用`npm install`命令来安装包。 首先全局安装`gulp-cli`(如果你使用 Unix 系统,你可能需要在`npm install`命令上使用`sudo`)。 ```text npm install -g gulp-cli ``` -然后安装`typescript`,`gulp`和`gulp-typescript`到开发依赖项。 [Gulp-typescript](https://www.npmjs.com/package/gulp-typescript)是TypeScript的一个Gulp插件。 +然后安装`typescript`,`gulp`和`gulp-typescript`到开发依赖项。 [Gulp-typescript](https://www.npmjs.com/package/gulp-typescript)是 TypeScript 的一个 Gulp 插件。 ```text npm install --save-dev typescript gulp@4.0.0 gulp-typescript @@ -56,11 +56,11 @@ npm install --save-dev typescript gulp@4.0.0 gulp-typescript ### 写一个简单的例子 -让我们写一个Hello World程序。 在`src`目录下创建`main.ts`文件: +让我们写一个 Hello World 程序。 在`src`目录下创建`main.ts`文件: ```typescript function hello(compiler: string) { - console.log(`Hello from ${compiler}`); + console.log(`Hello from ${compiler}`); } hello('TypeScript'); ``` @@ -89,9 +89,7 @@ var ts = require('gulp-typescript'); var tsProject = ts.createProject('tsconfig.json'); gulp.task('default', function () { - return tsProject.src() - .pipe(tsProject()) - .js.pipe(gulp.dest('dist')); + return tsProject.src().pipe(tsProject()).js.pipe(gulp.dest('dist')); }); ``` @@ -106,13 +104,13 @@ node dist/main.js ## 向代码里添加模块 -在使用Browserify前,让我们先构建一下代码然后再添加一些混入的模块。 这个结构将是你在真实应用程序中会用到的。 +在使用 Browserify 前,让我们先构建一下代码然后再添加一些混入的模块。 这个结构将是你在真实应用程序中会用到的。 新建一个`src/greet.ts`文件: ```typescript export function sayHello(name: string) { - return `Hello from ${name}`; + return `Hello from ${name}`; } ``` @@ -139,20 +137,20 @@ console.log(sayHello('TypeScript')); } ``` -确保执行`gulp`后模块是能工作的,在Node.js下进行测试: +确保执行`gulp`后模块是能工作的,在 Node.js 下进行测试: ```text gulp node dist/main.js ``` -注意,即使我们使用了ES2015的模块语法,TypeScript还是会生成Node.js使用的CommonJS模块。 我们在这个教程里会一直使用CommonJS模块,但是你可以通过修改`module`选项来改变这个行为。 +注意,即使我们使用了 ES2015 的模块语法,TypeScript 还是会生成 Node.js 使用的 CommonJS 模块。 我们在这个教程里会一直使用 CommonJS 模块,但是你可以通过修改`module`选项来改变这个行为。 ## Browserify -现在,让我们把这个工程由Node.js环境移到浏览器环境里。 因此,我们将把所有模块捆绑成一个JavaScript文件。 所幸,这正是Browserify要做的事情。 更方便的是,它支持Node.js的CommonJS模块,这也正是TypeScript默认生成的类型。 也就是说TypeScript和Node.js的设置不需要改变就可以移植到浏览器里。 +现在,让我们把这个工程由 Node.js 环境移到浏览器环境里。 因此,我们将把所有模块捆绑成一个 JavaScript 文件。 所幸,这正是 Browserify 要做的事情。 更方便的是,它支持 Node.js 的 CommonJS 模块,这也正是 TypeScript 默认生成的类型。 也就是说 TypeScript 和 Node.js 的设置不需要改变就可以移植到浏览器里。 -首先,安装Browserify,[tsify](https://www.npmjs.com/package/tsify)和vinyl-source-stream。 tsify是Browserify的一个插件,就像gulp-typescript一样,它能够访问TypeScript编译器。 vinyl-source-stream会将Browserify的输出文件适配成gulp能够解析的格式,它叫做[vinyl](https://github.com/gulpjs/vinyl)。 +首先,安装 Browserify,[tsify](https://www.npmjs.com/package/tsify)和 vinyl-source-stream。 tsify 是 Browserify 的一个插件,就像 gulp-typescript 一样,它能够访问 TypeScript 编译器。 vinyl-source-stream 会将 Browserify 的输出文件适配成 gulp 能够解析的格式,它叫做[vinyl](https://github.com/gulpjs/vinyl)。 ```text npm install --save-dev browserify tsify vinyl-source-stream @@ -182,14 +180,14 @@ npm install --save-dev browserify tsify vinyl-source-stream import { sayHello } from './greet'; function showHello(divName: string, name: string) { - const elt = document.getElementById(divName); - elt.innerText = sayHello(name); + const elt = document.getElementById(divName); + elt.innerText = sayHello(name); } showHello('greeting', 'TypeScript'); ``` -`showHello`调用`sayHello`函数更改页面上段落的文字。 现在修改gulpfile文件如下: +`showHello`调用`sayHello`函数更改页面上段落的文字。 现在修改 gulpfile 文件如下: ```javascript var gulp = require('gulp'); @@ -197,54 +195,56 @@ var browserify = require('browserify'); var source = require('vinyl-source-stream'); var tsify = require('tsify'); var paths = { - pages: ['src/*.html'] + pages: ['src/*.html'], }; gulp.task('copy-html', function () { - return gulp.src(paths.pages) - .pipe(gulp.dest('dist')); + return gulp.src(paths.pages).pipe(gulp.dest('dist')); }); -gulp.task('default', gulp.series(gulp.parallel('copy-html'), function () { +gulp.task( + 'default', + gulp.series(gulp.parallel('copy-html'), function () { return browserify({ - basedir: '.', - debug: true, - entries: ['src/main.ts'], - cache: {}, - packageCache: {} + basedir: '.', + debug: true, + entries: ['src/main.ts'], + cache: {}, + packageCache: {}, }) - .plugin(tsify) - .bundle() - .pipe(source('bundle.js')) - .pipe(gulp.dest('dist')); -})); + .plugin(tsify) + .bundle() + .pipe(source('bundle.js')) + .pipe(gulp.dest('dist')); + }) +); ``` -这里增加了`copy-html`任务并且把它加作`default`的依赖项。 这样,当`default`执行时,`copy-html`会被首先执行。 我们还修改了`default`任务,让它使用`tsify`插件调用Browserify,而不是`gulp-typescript`。 方便的是,两者传递相同的参数对象到TypeScript编译器。 +这里增加了`copy-html`任务并且把它加作`default`的依赖项。 这样,当`default`执行时,`copy-html`会被首先执行。 我们还修改了`default`任务,让它使用`tsify`插件调用 Browserify,而不是`gulp-typescript`。 方便的是,两者传递相同的参数对象到 TypeScript 编译器。 -调用`bundle`后,我们使用`source`(vinyl-source-stream的别名)把输出文件命名为`bundle.js`。 +调用`bundle`后,我们使用`source`(vinyl-source-stream 的别名)把输出文件命名为`bundle.js`。 测试此页面,运行`gulp`,然后在浏览器里打开`dist/index.html`。 你应该能在页面上看到“Hello from TypeScript”。 -注意,我们为Broswerify指定了`debug: true`。 这会让`tsify`在输出文件里生成`source maps`。 `source maps`允许我们在浏览器中直接调试TypeScript源码,而不是在合并后的JavaScript文件上调试。 你要打开调试器并在`main.ts`里打一个断点,看看`source maps`是否能工作。 当你刷新页面时,代码会停在断点处,从而你就能够调试`greet.ts`。 +注意,我们为 Broswerify 指定了`debug: true`。 这会让`tsify`在输出文件里生成`source maps`。 `source maps`允许我们在浏览器中直接调试 TypeScript 源码,而不是在合并后的 JavaScript 文件上调试。 你要打开调试器并在`main.ts`里打一个断点,看看`source maps`是否能工作。 当你刷新页面时,代码会停在断点处,从而你就能够调试`greet.ts`。 -## Watchify,Babel和Uglify +## Watchify,Babel 和 Uglify -现在代码已经用Browserify和tsify捆绑在一起了,我们可以使用Browserify插件为构建添加一些特性。 +现在代码已经用 Browserify 和 tsify 捆绑在一起了,我们可以使用 Browserify 插件为构建添加一些特性。 -* Watchify启动Gulp并保持运行状态,当你保存文件时自动编译。 帮你进入到编辑-保存-刷新浏览器的循环中。 -* Babel是个十分灵活的编译器,将ES2015及以上版本的代码转换成ES5和ES3。 你可以添加大量自定义的TypeScript目前不支持的转换器。 -* Uglify帮你压缩代码,将花费更少的时间去下载它们。 +- Watchify 启动 Gulp 并保持运行状态,当你保存文件时自动编译。 帮你进入到编辑-保存-刷新浏览器的循环中。 +- Babel 是个十分灵活的编译器,将 ES2015 及以上版本的代码转换成 ES5 和 ES3。 你可以添加大量自定义的 TypeScript 目前不支持的转换器。 +- Uglify 帮你压缩代码,将花费更少的时间去下载它们。 ### Watchify -我们启动Watchify,让它在后台帮我们编译: +我们启动 Watchify,让它在后台帮我们编译: ```text npm install --save-dev watchify fancy-log ``` -修改gulpfile文件如下: +修改 gulpfile 文件如下: ```javascript var gulp = require('gulp'); @@ -254,28 +254,29 @@ var watchify = require('watchify'); var tsify = require('tsify'); var fancy_log = require('fancy-log'); var paths = { - pages: ['src/*.html'] + pages: ['src/*.html'], }; -var watchedBrowserify = watchify(browserify({ +var watchedBrowserify = watchify( + browserify({ basedir: '.', debug: true, entries: ['src/main.ts'], cache: {}, - packageCache: {} -}).plugin(tsify)); + packageCache: {}, + }).plugin(tsify) +); gulp.task('copy-html', function () { - return gulp.src(paths.pages) - .pipe(gulp.dest('dist')); + return gulp.src(paths.pages).pipe(gulp.dest('dist')); }); function bundle() { - return watchedBrowserify - .bundle() - .on('error', fancy_log) - .pipe(source('bundle.js')) - .pipe(gulp.dest('dist')); + return watchedBrowserify + .bundle() + .on('error', fancy_log) + .pipe(source('bundle.js')) + .pipe(gulp.dest('dist')); } gulp.task('default', gulp.series(gulp.parallel('copy-html'), bundle)); @@ -286,10 +287,10 @@ watchedBrowserify.on('log', fancy_log); 共有三处改变,但是需要你略微重构一下代码。 1. 将`browserify`实例包裹在`watchify`的调用里,控制生成的结果。 -2. 调用`watchedBrowserify.on('update', bundle);`,每次TypeScript文件改变时Browserify会执行`bundle`函数。 +2. 调用`watchedBrowserify.on('update', bundle);`,每次 TypeScript 文件改变时 Browserify 会执行`bundle`函数。 3. 调用`watchedBrowserify.on('log', fancy_log);`将日志打印到控制台。 -\(1\)和\(2\)在一起意味着我们要将`browserify`调用移出`default`任务。 然后给函数起个名字,因为Watchify和Gulp都要调用它。 \(3\)是可选的,但是对于调试来讲很有用。 +\(1\)和\(2\)在一起意味着我们要将`browserify`调用移出`default`任务。 然后给函数起个名字,因为 Watchify 和 Gulp 都要调用它。 \(3\)是可选的,但是对于调试来讲很有用。 现在当你执行`gulp`,它会启动并保持运行状态。 试着改变`main.ts`文件里`showHello`的代码并保存。 你会看到这样的输出: @@ -307,13 +308,13 @@ proj$ gulp ### Uglify -首先安装Uglify。 因为Uglify是用于混淆你的代码,所以我们还要安装vinyl-buffer和gulp-sourcemaps来支持sourcemaps。 +首先安装 Uglify。 因为 Uglify 是用于混淆你的代码,所以我们还要安装 vinyl-buffer 和 gulp-sourcemaps 来支持 sourcemaps。 ```text npm install --save-dev gulp-uglify vinyl-buffer gulp-sourcemaps ``` -修改gulpfile文件如下: +修改 gulpfile 文件如下: ```javascript var gulp = require('gulp'); @@ -324,34 +325,36 @@ var uglify = require('gulp-uglify'); var sourcemaps = require('gulp-sourcemaps'); var buffer = require('vinyl-buffer'); var paths = { - pages: ['src/*.html'] + pages: ['src/*.html'], }; gulp.task('copy-html', function () { - return gulp.src(paths.pages) - .pipe(gulp.dest('dist')); + return gulp.src(paths.pages).pipe(gulp.dest('dist')); }); -gulp.task('default', gulp.series(gulp.parallel('copy-html'), function () { +gulp.task( + 'default', + gulp.series(gulp.parallel('copy-html'), function () { return browserify({ - basedir: '.', - debug: true, - entries: ['src/main.ts'], - cache: {}, - packageCache: {} + basedir: '.', + debug: true, + entries: ['src/main.ts'], + cache: {}, + packageCache: {}, }) - .plugin(tsify) - .bundle() - .pipe(source('bundle.js')) - .pipe(buffer()) - .pipe(sourcemaps.init({loadMaps: true})) - .pipe(uglify()) - .pipe(sourcemaps.write('./')) - .pipe(gulp.dest('dist')); -})); + .plugin(tsify) + .bundle() + .pipe(source('bundle.js')) + .pipe(buffer()) + .pipe(sourcemaps.init({ loadMaps: true })) + .pipe(uglify()) + .pipe(sourcemaps.write('./')) + .pipe(gulp.dest('dist')); + }) +); ``` -注意`uglify`只是调用了自己—`buffer`和`sourcemaps`的调用是用于确保sourcemaps可以工作。 这些调用让我们可以使用单独的sourcemap文件,而不是之前的内嵌的sourcemaps。 你现在可以执行`gulp`来检查`bundle.js`是否被压缩了: +注意`uglify`只是调用了自己—`buffer`和`sourcemaps`的调用是用于确保 sourcemaps 可以工作。 这些调用让我们可以使用单独的 sourcemap 文件,而不是之前的内嵌的 sourcemaps。 你现在可以执行`gulp`来检查`bundle.js`是否被压缩了: ```text gulp @@ -360,13 +363,13 @@ cat dist/bundle.js ### Babel -首先安装Babelify和ES2015的Babel预置程序。 和Uglify一样,Babelify也会混淆代码,因此我们也需要vinyl-buffer和gulp-sourcemaps。 默认情况下Babelify只会处理扩展名为`.js`,`.es`,`.es6`和`.jsx`的文件,因此我们需要添加`.ts`扩展名到Babelify选项。 +首先安装 Babelify 和 ES2015 的 Babel 预置程序。 和 Uglify 一样,Babelify 也会混淆代码,因此我们也需要 vinyl-buffer 和 gulp-sourcemaps。 默认情况下 Babelify 只会处理扩展名为`.js`,`.es`,`.es6`和`.jsx`的文件,因此我们需要添加`.ts`扩展名到 Babelify 选项。 ```text npm install --save-dev babelify@8 babel-core babel-preset-es2015 vinyl-buffer gulp-sourcemaps ``` -修改gulpfile文件如下: +修改 gulpfile 文件如下: ```javascript var gulp = require('gulp'); @@ -376,37 +379,39 @@ var tsify = require('tsify'); var sourcemaps = require('gulp-sourcemaps'); var buffer = require('vinyl-buffer'); var paths = { - pages: ['src/*.html'] + pages: ['src/*.html'], }; gulp.task('copyHtml', function () { - return gulp.src(paths.pages) - .pipe(gulp.dest('dist')); + return gulp.src(paths.pages).pipe(gulp.dest('dist')); }); -gulp.task('default', gulp.series(gulp.parallel('copy-html'), function () { +gulp.task( + 'default', + gulp.series(gulp.parallel('copy-html'), function () { return browserify({ - basedir: '.', - debug: true, - entries: ['src/main.ts'], - cache: {}, - packageCache: {} + basedir: '.', + debug: true, + entries: ['src/main.ts'], + cache: {}, + packageCache: {}, }) - .plugin(tsify) - .transform('babelify', { + .plugin(tsify) + .transform('babelify', { presets: ['es2015'], - extensions: ['.ts'] - }) - .bundle() - .pipe(source('bundle.js')) - .pipe(buffer()) - .pipe(sourcemaps.init({loadMaps: true})) - .pipe(sourcemaps.write('./')) - .pipe(gulp.dest('dist')); -})); + extensions: ['.ts'], + }) + .bundle() + .pipe(source('bundle.js')) + .pipe(buffer()) + .pipe(sourcemaps.init({ loadMaps: true })) + .pipe(sourcemaps.write('./')) + .pipe(gulp.dest('dist')); + }) +); ``` -我们需要设置TypeScript目标为ES2015。 Babel稍后会从TypeScript生成的ES2015代码中生成ES5。 修改`tsconfig.json`: +我们需要设置 TypeScript 目标为 ES2015。 Babel 稍后会从 TypeScript 生成的 ES2015 代码中生成 ES5。 修改`tsconfig.json`: ```javascript { @@ -420,5 +425,4 @@ gulp.task('default', gulp.series(gulp.parallel('copy-html'), function () { } ``` -对于这样一段简单的代码来说,Babel的ES5输出应该和TypeScript的输出相似。 - +对于这样一段简单的代码来说,Babel 的 ES5 输出应该和 TypeScript 的输出相似。 diff --git a/zh/tutorials/knockout.md b/zh/tutorials/knockout.md index a0a98e1b..da29aab5 100644 --- a/zh/tutorials/knockout.md +++ b/zh/tutorials/knockout.md @@ -2,7 +2,7 @@ > 注意: 此教程已从官方删除 -这个快速上手指南会告诉你如何结合使用TypeScript和[Knockout.js](http://knockoutjs.com/)。 +这个快速上手指南会告诉你如何结合使用 TypeScript 和[Knockout.js](http://knockoutjs.com/)。 这里我们假设你已经会使用[Node.js](https://nodejs.org/)和[npm](https://www.npmjs.com/) @@ -23,7 +23,7 @@ proj/ └─ built/ ``` -TypeScript源码放在`src`目录下,结过TypeScript编译器编译后,生成的文件放在`built`目录里。 +TypeScript 源码放在`src`目录下,结过 TypeScript 编译器编译后,生成的文件放在`built`目录里。 下面创建目录: @@ -34,7 +34,7 @@ mkdir built ## 初始化工程 -现在将这个文件夹转换为npm包。 +现在将这个文件夹转换为 npm 包。 ```text npm init @@ -44,13 +44,13 @@ npm init ## 安装构建依赖 -首先确保TypeScript已经全局安装。 +首先确保 TypeScript 已经全局安装。 ```text npm install -g typescript ``` -我们还要获取Knockout的声明文件,它描述了这个库的结构供TypeScript使用。 +我们还要获取 Knockout 的声明文件,它描述了这个库的结构供 TypeScript 使用。 ```text npm install --save @types/knockout @@ -58,7 +58,7 @@ npm install --save @types/knockout ## 获取运行时依赖 -我们需要Knockout和RequireJS。 [RequireJS](http://www.requirejs.org/)是一个库,它可以让我们在运行时异步地加载模块。 +我们需要 Knockout 和 RequireJS。 [RequireJS](http://www.requirejs.org/)是一个库,它可以让我们在运行时异步地加载模块。 有以下几种获取方式: @@ -66,7 +66,7 @@ npm install --save @types/knockout 2. 通过像[Bower](http://bower.io/)这样的包管理下载并维护它们。 3. 使用内容分发网络(CDN)来维护这两个文件。 -我们使用第一种方法,它会简单一些,但是Knockout的官方文档上有讲解[如何使用CDN](http://knockoutjs.com/downloads/index.html),更多像RequireJS一样的代码库可以在[cdnjs](https://cdnjs.com/)上查找。 +我们使用第一种方法,它会简单一些,但是 Knockout 的官方文档上有讲解[如何使用 CDN](http://knockoutjs.com/downloads/index.html),更多像 RequireJS 一样的代码库可以在[cdnjs](https://cdnjs.com/)上查找。 下面让我们在工程根目录下创建`externals`目录。 @@ -74,11 +74,11 @@ npm install --save @types/knockout mkdir externals ``` -然后[下载Knockout](http://knockoutjs.com/downloads/index.html)和[下载RequireJS](http://www.requirejs.org/docs/download.html#latest)到这个目录里。 最新的压缩后版本就可以。 +然后[下载 Knockout](http://knockoutjs.com/downloads/index.html)和[下载 RequireJS](http://www.requirejs.org/docs/download.html#latest)到这个目录里。 最新的压缩后版本就可以。 -## 添加TypeScript配置文件 +## 添加 TypeScript 配置文件 -下面我们想把所有的TypeScript文件整合到一起 - 包括自己写的和必须的声明文件。 +下面我们想把所有的 TypeScript 文件整合到一起 - 包括自己写的和必须的声明文件。 我们需要创建一个`tsconfig.json`文件,包含了输入文件列表和编译选项。 在工程根目录下创建一个新文件`tsconfig.json`,内容如下: @@ -98,7 +98,7 @@ mkdir externals } ``` -这里引用了`typings/index.d.ts`,它是Typings帮我们创建的。 这个文件会自动地包含所有安装的依赖。 +这里引用了`typings/index.d.ts`,它是 Typings 帮我们创建的。 这个文件会自动地包含所有安装的依赖。 你可能会对`typings`目录下的`browser.d.ts`文件感到好奇,尤其因为我们将在浏览器里运行代码。 其实原因是这样的,当目标为浏览器的时候,一些包会生成不同的版本。 通常来讲,这些情况很少发生并且在这里我们不会遇到这种情况,所以我们可以忽略`browser.d.ts`。 @@ -106,22 +106,22 @@ mkdir externals ## 写些代码 -下面我们使用Knockout写一段TypeScript代码。 首先,在`src`目录里新建一个`hello.ts`文件。 +下面我们使用 Knockout 写一段 TypeScript 代码。 首先,在`src`目录里新建一个`hello.ts`文件。 ```typescript -import * as ko from "knockout"; +import * as ko from 'knockout'; class HelloViewModel { - language: KnockoutObservable - framework: KnockoutObservable + language: KnockoutObservable; + framework: KnockoutObservable; - constructor(language: string, framework: string) { - this.language = ko.observable(language); - this.framework = ko.observable(framework); - } + constructor(language: string, framework: string) { + this.language = ko.observable(language); + this.framework = ko.observable(framework); + } } -ko.applyBindings(new HelloViewModel("TypeScript", "Knockout")); +ko.applyBindings(new HelloViewModel('TypeScript', 'Knockout')); ``` 接下来,在`src`目录下再新建一个`require-config.ts`文件。 @@ -129,13 +129,13 @@ ko.applyBindings(new HelloViewModel("TypeScript", "Knockout")); ```typescript declare var require: any; require.config({ - paths: { - "knockout": "externals/knockout-3.4.0", - } + paths: { + knockout: 'externals/knockout-3.4.0', + }, }); ``` -这个文件会告诉RequireJS从哪里导入Knockout,好比我们在`hello.ts`里做的一样。 你创建的所有页面都应该在RequireJS之后和导入任何东西之前引入它。 为了更好地理解这个文件和如何配置RequireJS,可以查看[文档](http://requirejs.org/docs/api.html#config)。 +这个文件会告诉 RequireJS 从哪里导入 Knockout,好比我们在`hello.ts`里做的一样。 你创建的所有页面都应该在 RequireJS 之后和导入任何东西之前引入它。 为了更好地理解这个文件和如何配置 RequireJS,可以查看[文档](http://requirejs.org/docs/api.html#config)。 我们还需要一个视图来显示`HelloViewModel`。 在`proj`目录的根上创建一个文件`index.html`,内容如下: @@ -166,7 +166,7 @@ require.config({ ``` -注意,有两个script标签。 首先,我们引入RequireJS。 然后我们再在`require-config.js`里映射外部依赖,这样RequireJS就能知道到哪里去查找它们。 最后,使用我们要去加载的模块去调用`require`。 +注意,有两个 script 标签。 首先,我们引入 RequireJS。 然后我们再在`require-config.js`里映射外部依赖,这样 RequireJS 就能知道到哪里去查找它们。 最后,使用我们要去加载的模块去调用`require`。 ## 将所有部分整合在一起 @@ -177,4 +177,3 @@ tsc ``` 现在,在你喜欢的浏览器打开`index.html`,所有都应该好用了。 你应该可以看到页面上显示“Hello from TypeScript and Knockout!”。 在它下面,你还会看到两个输入框。 当你改变输入和切换焦点时,就会看到原先显示的信息改变了。 - diff --git a/zh/tutorials/migrating-from-javascript.md b/zh/tutorials/migrating-from-javascript.md index bf7078fa..ef6e7c5b 100644 --- a/zh/tutorials/migrating-from-javascript.md +++ b/zh/tutorials/migrating-from-javascript.md @@ -1,16 +1,16 @@ -# 从JavaScript迁移到TypeScript +# 从 JavaScript 迁移到 TypeScript -TypeScript不是凭空存在的。 它从JavaScript生态系统和大量现存的JavaScript而来。 将JavaScript代码转换成TypeScript虽乏味却不是难事。 接下来这篇教程将教你怎么做。 在开始转换TypeScript之前,我们假设你已经理解了足够多本手册里的内容。 +TypeScript 不是凭空存在的。 它从 JavaScript 生态系统和大量现存的 JavaScript 而来。 将 JavaScript 代码转换成 TypeScript 虽乏味却不是难事。 接下来这篇教程将教你怎么做。 在开始转换 TypeScript 之前,我们假设你已经理解了足够多本手册里的内容。 -如果你打算要转换一个React工程,推荐你先阅读[React转换指南](https://github.com/Microsoft/TypeScript-React-Conversion-Guide#typescript-react-conversion-guide)。 +如果你打算要转换一个 React 工程,推荐你先阅读[React 转换指南](https://github.com/Microsoft/TypeScript-React-Conversion-Guide#typescript-react-conversion-guide)。 ## 设置目录 -如果你在写纯JavaScript,你大概是想直接运行这些JavaScript文件, 这些文件存在于`src`,`lib`或`dist`目录里,它们可以按照预想运行。 +如果你在写纯 JavaScript,你大概是想直接运行这些 JavaScript 文件, 这些文件存在于`src`,`lib`或`dist`目录里,它们可以按照预想运行。 -若如此,那么你写的纯JavaScript文件将做为TypeScript的输入,你将要运行的是TypeScript的输出。 在从JS到TS的转换过程中,我们会分离输入文件以防TypeScript覆盖它们。 你也可以指定输出目录。 +若如此,那么你写的纯 JavaScript 文件将做为 TypeScript 的输入,你将要运行的是 TypeScript 的输出。 在从 JS 到 TS 的转换过程中,我们会分离输入文件以防 TypeScript 覆盖它们。 你也可以指定输出目录。 -你可能还需要对JavaScript做一些中间处理,比如合并或经过Babel再次编译。 在这种情况下,你应该已经有了如下的目录结构。 +你可能还需要对 JavaScript 做一些中间处理,比如合并或经过 Babel 再次编译。 在这种情况下,你应该已经有了如下的目录结构。 那么现在,我们假设你已经设置了这样的目录结构: @@ -27,7 +27,7 @@ projectRoot ## 书写配置文件 -TypeScript使用`tsconfig.json`文件管理工程配置,例如你想包含哪些文件和进行哪些检查。 让我们先创建一个简单的工程配置文件: +TypeScript 使用`tsconfig.json`文件管理工程配置,例如你想包含哪些文件和进行哪些检查。 让我们先创建一个简单的工程配置文件: ```javascript { @@ -42,23 +42,23 @@ TypeScript使用`tsconfig.json`文件管理工程配置,例如你想包含哪 } ``` -这里我们为TypeScript设置了一些东西: +这里我们为 TypeScript 设置了一些东西: 1. 读取所有可识别的`src`目录下的文件(通过`include`)。 -2. 接受JavaScript做为输入(通过`allowJs`)。 +2. 接受 JavaScript 做为输入(通过`allowJs`)。 3. 生成的所有文件放在`built`目录下(通过`outDir`)。 -4. 将JavaScript代码降级到低版本比如ECMAScript 5(通过`target`)。 +4. 将 JavaScript 代码降级到低版本比如 ECMAScript 5(通过`target`)。 -现在,如果你在工程根目录下运行`tsc`,就可以在`built`目录下看到生成的文件。 `built`下的文件应该与`src`下的文件相同。 现在你的工程里的TypeScript已经可以工作了。 +现在,如果你在工程根目录下运行`tsc`,就可以在`built`目录下看到生成的文件。 `built`下的文件应该与`src`下的文件相同。 现在你的工程里的 TypeScript 已经可以工作了。 ### 早期收益 -现在你已经可以看到TypeScript带来的好处,它能帮助我们理解当前工程。 如果你打开像[VS Code](https://code.visualstudio.com)或[Visual Studio](https://visualstudio.com)这样的编译器,你就能使用像自动补全这样的工具。 你还可以配置如下的选项来帮助查找BUG: +现在你已经可以看到 TypeScript 带来的好处,它能帮助我们理解当前工程。 如果你打开像[VS Code](https://code.visualstudio.com)或[Visual Studio](https://visualstudio.com)这样的编译器,你就能使用像自动补全这样的工具。 你还可以配置如下的选项来帮助查找 BUG: -* `noImplicitReturns` 会防止你忘记在函数末尾返回值。 -* `noFallthroughCasesInSwitch` 会防止在`switch`代码块里的两个`case`之间忘记添加`break`语句。 +- `noImplicitReturns` 会防止你忘记在函数末尾返回值。 +- `noFallthroughCasesInSwitch` 会防止在`switch`代码块里的两个`case`之间忘记添加`break`语句。 -TypeScript还能发现那些执行不到的代码和标签,你可以通过设置`allowUnreachableCode`和`allowUnusedLabels`选项来禁用。 +TypeScript 还能发现那些执行不到的代码和标签,你可以通过设置`allowUnreachableCode`和`allowUnusedLabels`选项来禁用。 ## 与构建工具进行集成 @@ -66,11 +66,11 @@ TypeScript还能发现那些执行不到的代码和标签,你可以通过设 ### Gulp -如果你在使用时髦的Gulp,我们已经有一篇关于[使用Gulp](gulp.md)结合TypeScript并与常见构建工具Browserify,Babelify和Uglify进行集成的教程。 请阅读这篇教程。 +如果你在使用时髦的 Gulp,我们已经有一篇关于[使用 Gulp](gulp.md)结合 TypeScript 并与常见构建工具 Browserify,Babelify 和 Uglify 进行集成的教程。 请阅读这篇教程。 ### Webpack -Webpack集成非常简单。 你可以使用`awesome-typescript-loader`,它是一个TypeScript的加载器,结合`source-map-loader`方便调试。 运行: +Webpack 集成非常简单。 你可以使用`awesome-typescript-loader`,它是一个 TypeScript 的加载器,结合`source-map-loader`方便调试。 运行: ```text npm install awesome-typescript-loader source-map-loader @@ -80,60 +80,60 @@ npm install awesome-typescript-loader source-map-loader ```javascript module.exports = { - entry: "./src/index.ts", - output: { - filename: "./dist/bundle.js", - }, - - // Enable sourcemaps for debugging webpack's output. - devtool: "source-map", - - resolve: { - // Add '.ts' and '.tsx' as resolvable extensions. - extensions: ["", ".webpack.js", ".web.js", ".ts", ".tsx", ".js"] - }, - - module: { - loaders: [ - // All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader'. - { test: /\.tsx?$/, loader: "awesome-typescript-loader" } - ], - - preLoaders: [ - // All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'. - { test: /\.js$/, loader: "source-map-loader" } - ] - }, - - // Other options... + entry: './src/index.ts', + output: { + filename: './dist/bundle.js', + }, + + // Enable sourcemaps for debugging webpack's output. + devtool: 'source-map', + + resolve: { + // Add '.ts' and '.tsx' as resolvable extensions. + extensions: ['', '.webpack.js', '.web.js', '.ts', '.tsx', '.js'], + }, + + module: { + loaders: [ + // All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader'. + { test: /\.tsx?$/, loader: 'awesome-typescript-loader' }, + ], + + preLoaders: [ + // All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'. + { test: /\.js$/, loader: 'source-map-loader' }, + ], + }, + + // Other options... }; ``` 要注意的是,`awesome-typescript-loader`必须在其它处理`.js`文件的加载器之前运行。 -这与另一个TypeScript的Webpack加载器[ts-loader](https://github.com/TypeStrong/ts-loader)是一样的。 你可以到[这里](https://github.com/s-panferov/awesome-typescript-loader#differences-between-ts-loader)了解两者之间的差别。 +这与另一个 TypeScript 的 Webpack 加载器[ts-loader](https://github.com/TypeStrong/ts-loader)是一样的。 你可以到[这里](https://github.com/s-panferov/awesome-typescript-loader#differences-between-ts-loader)了解两者之间的差别。 -你可以在[React和Webpack教程](react-and-webpack.md)里找到使用Webpack的例子。 +你可以在[React 和 Webpack 教程](react-and-webpack.md)里找到使用 Webpack 的例子。 -## 转换到TypeScript文件 +## 转换到 TypeScript 文件 -到目前为止,你已经做好了使用TypeScript文件的准备。 第一步,将`.js`文件重命名为`.ts`文件。 如果你使用了JSX,则重命名为`.tsx`文件。 +到目前为止,你已经做好了使用 TypeScript 文件的准备。 第一步,将`.js`文件重命名为`.ts`文件。 如果你使用了 JSX,则重命名为`.tsx`文件。 -第一步达成? 太棒了! 你已经成功地将一个文件从JavaScript转换成了TypeScript! +第一步达成? 太棒了! 你已经成功地将一个文件从 JavaScript 转换成了 TypeScript! -当然了,你可能感觉哪里不对劲儿。 如果你在支持TypeScript的编辑器(或运行`tsc --pretty`)里打开了那个文件,你可能会看到有些行上有红色的波浪线。 你可以把它们当做在Microsoft Word里看到的红色波浪线一样。 但是TypeScript仍然会编译你的代码,就好比Word还是允许你打印你的文档一样。 +当然了,你可能感觉哪里不对劲儿。 如果你在支持 TypeScript 的编辑器(或运行`tsc --pretty`)里打开了那个文件,你可能会看到有些行上有红色的波浪线。 你可以把它们当做在 Microsoft Word 里看到的红色波浪线一样。 但是 TypeScript 仍然会编译你的代码,就好比 Word 还是允许你打印你的文档一样。 -如果对你来说这种行为太随便了,你可以让它变得严格些。 如果,你_不想_在发生错误的时候,TypeScript还会被编译成JavaScript,你可以使用`noEmitOnError`选项。 从某种意义上来讲,TypeScript具有一个调整它的严格性的刻度盘,你可以将指针拔动到你想要的位置。 +如果对你来说这种行为太随便了,你可以让它变得严格些。 如果,你*不想*在发生错误的时候,TypeScript 还会被编译成 JavaScript,你可以使用`noEmitOnError`选项。 从某种意义上来讲,TypeScript 具有一个调整它的严格性的刻度盘,你可以将指针拔动到你想要的位置。 -如果你计划使用可用的高度严格的设置,最好现在就启用它们(查看[启用严格检查](migrating-from-javascript.md#getting-stricter-checks))。 比如,如果你不想让TypeScript将没有明确指定的类型默默地推断为`any`类型,可以在修改文件之前启用`noImplicitAny`。 你可能会觉得这有些过度严格,但是长期收益很快就能显现出来。 +如果你计划使用可用的高度严格的设置,最好现在就启用它们(查看[启用严格检查](migrating-from-javascript.md#getting-stricter-checks))。 比如,如果你不想让 TypeScript 将没有明确指定的类型默默地推断为`any`类型,可以在修改文件之前启用`noImplicitAny`。 你可能会觉得这有些过度严格,但是长期收益很快就能显现出来。 ### 去除错误 -我们提到过,若不出所料,在转换后将会看到错误信息。 重要的是我们要逐一的查看它们并决定如何处理。 通常这些都是真正的BUG,但有时必须要告诉TypeScript你要做的是什么。 +我们提到过,若不出所料,在转换后将会看到错误信息。 重要的是我们要逐一的查看它们并决定如何处理。 通常这些都是真正的 BUG,但有时必须要告诉 TypeScript 你要做的是什么。 #### 由模块导入 -首先你可能会看到一些类似`Cannot find name 'require'.`和`Cannot find name 'define'.`的错误。 遇到这种情况说明你在使用模块。 你仅需要告诉TypeScript它们是存在的: +首先你可能会看到一些类似`Cannot find name 'require'.`和`Cannot find name 'define'.`的错误。 遇到这种情况说明你在使用模块。 你仅需要告诉 TypeScript 它们是存在的: ```typescript // For Node/CommonJS @@ -147,37 +147,37 @@ declare function require(path: string): any; declare function define(...args: any[]): any; ``` -最好是避免使用这些调用而改用TypeScript的导入语法。 +最好是避免使用这些调用而改用 TypeScript 的导入语法。 -首先,你要使用TypeScript的`module`标记来启用一些模块系统。 可用的选项有`commonjs`,`amd`,`system`,and `umd`。 +首先,你要使用 TypeScript 的`module`标记来启用一些模块系统。 可用的选项有`commonjs`,`amd`,`system`,and `umd`。 -如果代码里存在下面的Node/CommonJS代码: +如果代码里存在下面的 Node/CommonJS 代码: ```javascript -var foo = require("foo"); +var foo = require('foo'); foo.doStuff(); ``` -或者下面的RequireJS/AMD代码: +或者下面的 RequireJS/AMD 代码: ```javascript -define(["foo"], function(foo) { - foo.doStuff(); -}) +define(['foo'], function (foo) { + foo.doStuff(); +}); ``` -那么可以写做下面的TypeScript代码: +那么可以写做下面的 TypeScript 代码: ```typescript -import foo = require("foo"); +import foo = require('foo'); foo.doStuff(); ``` #### 获取声明文件 -如果你开始做转换到TypeScript导入,你可能会遇到`Cannot find module 'foo'.`这样的错误。 问题出在没有_声明文件_来描述你的代码库。 幸运的是这非常简单。 如果TypeScript报怨像是没有`lodash`包,那你只需这样做 +如果你开始做转换到 TypeScript 导入,你可能会遇到`Cannot find module 'foo'.`这样的错误。 问题出在没有*声明文件*来描述你的代码库。 幸运的是这非常简单。 如果 TypeScript 报怨像是没有`lodash`包,那你只需这样做 ```text npm install -S @types/lodash @@ -189,26 +189,26 @@ npm install -S @types/lodash #### 由模块导出 -通常来讲,由模块导出涉及添加属性到`exports`或`module.exports`。 TypeScript允许你使用顶级的导出语句。 比如,你要导出下面的函数: +通常来讲,由模块导出涉及添加属性到`exports`或`module.exports`。 TypeScript 允许你使用顶级的导出语句。 比如,你要导出下面的函数: ```javascript -module.exports.feedPets = function(pets) { - // ... -} +module.exports.feedPets = function (pets) { + // ... +}; ``` 那么你可以这样写: ```typescript export function feedPets(pets) { - // ... + // ... } ``` 有时你会完全重写导出对象。 这是一个常见模式,这会将模块变为可立即调用的模块: ```javascript -var express = require("express"); +var express = require('express'); var app = express(); ``` @@ -216,50 +216,63 @@ var app = express(); ```javascript function foo() { - // ... + // ... } module.exports = foo; ``` -在TypeScript里,你可以使用`export =`来代替。 +在 TypeScript 里,你可以使用`export =`来代替。 ```typescript function foo() { - // ... + // ... } export = foo; ``` #### 过多或过少的参数 -有时你会发现你在调用一个具有过多或过少参数的函数。 通常,这是一个BUG,但在某些情况下,你可以声明一个使用`arguments`对象的函数而不需要写出所有参数: +有时你会发现你在调用一个具有过多或过少参数的函数。 通常,这是一个 BUG,但在某些情况下,你可以声明一个使用`arguments`对象的函数而不需要写出所有参数: ```javascript function myCoolFunction() { - if (arguments.length == 2 && !Array.isArray(arguments[1])) { - var f = arguments[0]; - var arr = arguments[1]; - // ... - } + if (arguments.length == 2 && !Array.isArray(arguments[1])) { + var f = arguments[0]; + var arr = arguments[1]; // ... + } + // ... } -myCoolFunction(function(x) { console.log(x) }, [1, 2, 3, 4]); -myCoolFunction(function(x) { console.log(x) }, 1, 2, 3, 4); +myCoolFunction( + function (x) { + console.log(x); + }, + [1, 2, 3, 4] +); +myCoolFunction( + function (x) { + console.log(x); + }, + 1, + 2, + 3, + 4 +); ``` -这种情况下,我们需要利用TypeScript的函数重载来告诉调用者`myCoolFunction`函数的调用方式。 +这种情况下,我们需要利用 TypeScript 的函数重载来告诉调用者`myCoolFunction`函数的调用方式。 ```typescript function myCoolFunction(f: (x: number) => void, nums: number[]): void; function myCoolFunction(f: (x: number) => void, ...nums: number[]): void; function myCoolFunction() { - if (arguments.length == 2 && !Array.isArray(arguments[1])) { - var f = arguments[0]; - var arr = arguments[1]; - // ... - } + if (arguments.length == 2 && !Array.isArray(arguments[1])) { + var f = arguments[0]; + var arr = arguments[1]; // ... + } + // ... } ``` @@ -271,26 +284,29 @@ function myCoolFunction() { ```javascript var options = {}; -options.color = "red"; +options.color = 'red'; options.volume = 11; ``` -TypeScript会提示你不能给`color`和`volumn`赋值,因为先前指定`options`的类型为`{}`并不带有任何属性。 如果你将声明变成对象字面量的形式将不会产生错误: +TypeScript 会提示你不能给`color`和`volumn`赋值,因为先前指定`options`的类型为`{}`并不带有任何属性。 如果你将声明变成对象字面量的形式将不会产生错误: ```typescript let options = { - color: "red", - volume: 11 + color: 'red', + volume: 11, }; ``` 你还可以定义`options`的类型并且添加类型断言到对象字面量上。 ```typescript -interface Options { color: string; volume: number } +interface Options { + color: string; + volume: number; +} let options = {} as Options; -options.color = "red"; +options.color = 'red'; options.volume = 11; ``` @@ -298,34 +314,34 @@ options.volume = 11; #### `any`,`Object`,和`{}` -你可能会试图使用`Object`或`{}`来表示一个值可以具有任意属性,因为`Object`是最通用的类型。 然而在这种情况下**`any`是真正想要使用的类型**,因为它是最_灵活_的类型。 +你可能会试图使用`Object`或`{}`来表示一个值可以具有任意属性,因为`Object`是最通用的类型。 然而在这种情况下**`any`是真正想要使用的类型**,因为它是最*灵活*的类型。 比如,有一个`Object`类型的东西,你将不能够在其上调用`toLowerCase()`。 -越普通意味着更少的利用类型,但是`any`比较特殊,它是最普通的类型但是允许你在上面做任何事情。 也就是说你可以在上面调用,构造它,访问它的属性等等。 记住,当你使用`any`时,你会失去大多数TypeScript提供的错误检查和编译器支持。 +越普通意味着更少的利用类型,但是`any`比较特殊,它是最普通的类型但是允许你在上面做任何事情。 也就是说你可以在上面调用,构造它,访问它的属性等等。 记住,当你使用`any`时,你会失去大多数 TypeScript 提供的错误检查和编译器支持。 如果你还是决定使用`Object`和`{}`,你应该选择`{}`。 虽说它们基本一样,但是从技术角度上来讲`{}`在一些深奥的情况里比`Object`更普通。 ### 启用严格检查 -TypeScript提供了一些检查来保证安全以及帮助分析你的程序。 当你将代码转换为了TypeScript后,你可以启用这些检查来帮助你获得高度安全性。 +TypeScript 提供了一些检查来保证安全以及帮助分析你的程序。 当你将代码转换为了 TypeScript 后,你可以启用这些检查来帮助你获得高度安全性。 #### 没有隐式的`any` -在某些情况下TypeScript没法确定某些值的类型。 那么TypeScript会使用`any`类型代替。 这对代码转换来讲是不错,但是使用`any`意味着失去了类型安全保障,并且你得不到工具的支持。 你可以使用`noImplicitAny`选项,让TypeScript标记出发生这种情况的地方,并给出一个错误。 +在某些情况下 TypeScript 没法确定某些值的类型。 那么 TypeScript 会使用`any`类型代替。 这对代码转换来讲是不错,但是使用`any`意味着失去了类型安全保障,并且你得不到工具的支持。 你可以使用`noImplicitAny`选项,让 TypeScript 标记出发生这种情况的地方,并给出一个错误。 #### 严格的`null`与`undefined`检查 -默认地,TypeScript把`null`和`undefined`当做属于任何类型。 这就是说,声明为`number`类型的值可以为`null`和`undefined`。 因为在JavaScript和TypeScript里,`null`和`undefined`经常会导致BUG的产生,所以TypeScript包含了`strictNullChecks`选项来帮助我们减少对这种情况的担忧。 +默认地,TypeScript 把`null`和`undefined`当做属于任何类型。 这就是说,声明为`number`类型的值可以为`null`和`undefined`。 因为在 JavaScript 和 TypeScript 里,`null`和`undefined`经常会导致 BUG 的产生,所以 TypeScript 包含了`strictNullChecks`选项来帮助我们减少对这种情况的担忧。 -当启用了`strictNullChecks`,`null`和`undefined`获得了它们自己各自的类型`null`和`undefined`。 当任何值_可能_为`null`,你可以使用联合类型。 比如,某值可能为`number`或`null`,你可以声明它的类型为`number | null`。 +当启用了`strictNullChecks`,`null`和`undefined`获得了它们自己各自的类型`null`和`undefined`。 当任何值*可能*为`null`,你可以使用联合类型。 比如,某值可能为`number`或`null`,你可以声明它的类型为`number | null`。 -假设有一个值TypeScript认为可以为`null`或`undefined`,但是你更清楚它的类型,你可以使用`!`后缀。 +假设有一个值 TypeScript 认为可以为`null`或`undefined`,但是你更清楚它的类型,你可以使用`!`后缀。 ```typescript declare var foo: string[] | null; -foo.length; // error - 'foo' is possibly 'null' +foo.length; // error - 'foo' is possibly 'null' foo!.length; // okay - 'foo!' just has type 'string[]' ``` @@ -338,29 +354,28 @@ foo!.length; // okay - 'foo!' just has type 'string[]' ```typescript class Point { - constructor(public x, public y) {} - getDistance(p: Point) { - let dx = p.x - this.x; - let dy = p.y - this.y; - return Math.sqrt(dx ** 2 + dy ** 2); - } + constructor(public x, public y) {} + getDistance(p: Point) { + let dx = p.x - this.x; + let dy = p.y - this.y; + return Math.sqrt(dx ** 2 + dy ** 2); + } } // ... // Reopen the interface. interface Point { - distanceFromOrigin(point: Point): number; -} -Point.prototype.distanceFromOrigin = function(point: Point) { - return this.getDistance({ x: 0, y: 0}); + distanceFromOrigin(point: Point): number; } +Point.prototype.distanceFromOrigin = function (point: Point) { + return this.getDistance({ x: 0, y: 0 }); +}; ``` -这就产生了我们上面提到的错误 - 如果我们错误地拼写了`getDistance`并不会得到一个错误。 正因此,TypeScript有`noImplicitThis`选项。 当设置了它,TypeScript会产生一个错误当没有明确指定类型(或通过类型推断)的`this`被使用时。 解决的方法是在接口或函数上使用指定了类型的`this`参数: +这就产生了我们上面提到的错误 - 如果我们错误地拼写了`getDistance`并不会得到一个错误。 正因此,TypeScript 有`noImplicitThis`选项。 当设置了它,TypeScript 会产生一个错误当没有明确指定类型(或通过类型推断)的`this`被使用时。 解决的方法是在接口或函数上使用指定了类型的`this`参数: ```typescript -Point.prototype.distanceFromOrigin = function(this: Point, point: Point) { - return this.getDistance({ x: 0, y: 0}); -} +Point.prototype.distanceFromOrigin = function (this: Point, point: Point) { + return this.getDistance({ x: 0, y: 0 }); +}; ``` - diff --git a/zh/tutorials/react-and-webpack.md b/zh/tutorials/react-and-webpack.md index e10cd7e4..179953a0 100644 --- a/zh/tutorials/react-and-webpack.md +++ b/zh/tutorials/react-and-webpack.md @@ -1,8 +1,8 @@ -# React与webpack +# React 与 webpack -这篇指南将会教你如何将TypeScript和[React](https://reactjs.org/)还有[webpack](http://webpack.github.io/)结合在一起使用。 +这篇指南将会教你如何将 TypeScript 和[React](https://reactjs.org/)还有[webpack](http://webpack.github.io/)结合在一起使用。 -如果你正在做一个全新的工程,可以先阅读这篇[React快速上手指南](react.md)。 +如果你正在做一个全新的工程,可以先阅读这篇[React 快速上手指南](react.md)。 否则,我们假设已经在使用[Node.js](https://nodejs.org/)和[npm](https://www.npmjs.com/)。 @@ -24,7 +24,7 @@ proj/ └─ components/ ``` -TypeScript文件会放在`src`文件夹里,通过TypeScript编译器编译,然后经webpack处理,最后生成一个`main.js`文件放在`dist`目录下。 我们自定义的组件将会放在`src/components`文件夹下。 +TypeScript 文件会放在`src`文件夹里,通过 TypeScript 编译器编译,然后经 webpack 处理,最后生成一个`main.js`文件放在`dist`目录下。 我们自定义的组件将会放在`src/components`文件夹下。 下面来创建基本结构: @@ -35,11 +35,11 @@ mkdir components cd .. ``` -Webpack会帮助我们生成`dist`目录。 +Webpack 会帮助我们生成`dist`目录。 ## 初始化工程 -现在把这个目录变成npm包。 +现在把这个目录变成 npm 包。 ```text npm init -y @@ -49,22 +49,22 @@ npm init -y ## 安装依赖 -首先确保已经全局安装了Webpack。 +首先确保已经全局安装了 Webpack。 ```text npm install --save-dev webpack webpack-cli ``` -Webpack这个工具可以将你的所有代码和可选择地将依赖捆绑成一个单独的`.js`文件。 +Webpack 这个工具可以将你的所有代码和可选择地将依赖捆绑成一个单独的`.js`文件。 -现在我们添加React和React-DOM以及它们的声明文件到`package.json`文件里做为依赖: +现在我们添加 React 和 React-DOM 以及它们的声明文件到`package.json`文件里做为依赖: ```text npm install --save react react-dom npm install --save-dev @types/react @types/react-dom ``` -使用`@types/`前缀表示我们额外要获取React和React-DOM的声明文件。 通常当你导入像`"react"`这样的路径,它会查看`react`包; 然而,并不是所有的包都包含了声明文件,所以TypeScript还会查看`@types/react`包。 你会发现我们以后将不必在意这些。 +使用`@types/`前缀表示我们额外要获取 React 和 React-DOM 的声明文件。 通常当你导入像`"react"`这样的路径,它会查看`react`包; 然而,并不是所有的包都包含了声明文件,所以 TypeScript 还会查看`@types/react`包。 你会发现我们以后将不必在意这些。 接下来,我们要添加开发时依赖[ts-loader](https://www.npmjs.com/package/ts-loader)和[source-map-loader](https://www.npmjs.com/package/source-map-loader)。 @@ -72,17 +72,17 @@ npm install --save-dev @types/react @types/react-dom npm install --save-dev typescript ts-loader source-map-loader ``` -这些依赖会让TypeScript和webpack在一起良好地工作。 `ts-loader`可以让Webpack使用TypeScript的标准配置文件`tsconfig.json`编译TypeScript代码。 source-map-loader使用TypeScript输出的sourcemap文件来告诉webpack何时生成_自己的_sourcemaps。 这就允许你在调试最终生成的文件时就好像在调试TypeScript源码一样。 +这些依赖会让 TypeScript 和 webpack 在一起良好地工作。 `ts-loader`可以让 Webpack 使用 TypeScript 的标准配置文件`tsconfig.json`编译 TypeScript 代码。 source-map-loader 使用 TypeScript 输出的 sourcemap 文件来告诉 webpack 何时生成\_自己的\_sourcemaps。 这就允许你在调试最终生成的文件时就好像在调试 TypeScript 源码一样。 请注意,`ts-loader`并不是唯一的`TypeScript`加载器。 你还可以选择[awesome-typescript-loader](https://www.npmjs.com/package/awesome-typescript-loader)。 可以到[这里](https://github.com/s-panferov/awesome-typescript-loader#differences-between-ts-loader)查看它们之间的区别。 -注意我们安装TypeScript为一个开发依赖。 我们还可以使用`npm link typescript`来链接TypeScript到一个全局拷贝,但这不是常见用法。 +注意我们安装 TypeScript 为一个开发依赖。 我们还可以使用`npm link typescript`来链接 TypeScript 到一个全局拷贝,但这不是常见用法。 -## 添加TypeScript配置文件 +## 添加 TypeScript 配置文件 -我们想将TypeScript文件整合到一起 - 这包括我们写的源码和必要的声明文件。 +我们想将 TypeScript 文件整合到一起 - 这包括我们写的源码和必要的声明文件。 我们需要创建一个`tsconfig.json`文件,它包含了输入文件列表以及编译选项。 在工程根目录下新建文件`tsconfig.json`文件,添加以下内容: @@ -103,47 +103,61 @@ npm install --save-dev typescript ts-loader source-map-loader ## 写些代码 -下面使用React写一段TypeScript代码。 首先,在`src/components`目录下创建一个名为`Hello.tsx`的文件,代码如下: +下面使用 React 写一段 TypeScript 代码。 首先,在`src/components`目录下创建一个名为`Hello.tsx`的文件,代码如下: ```typescript -import * as React from "react"; +import * as React from 'react'; -export interface HelloProps { compiler: string; framework: string; } +export interface HelloProps { + compiler: string; + framework: string; +} -export const Hello = (props: HelloProps) =>

Hello from {props.compiler} and {props.framework}!

; +export const Hello = (props: HelloProps) => ( +

+ Hello from {props.compiler} and {props.framework}! +

+); ``` -注意这个例子使用了[函数组件](https://reactjs.org/docs/components-and-props.html#functional-and-class-components),我们可以让它更像一点_类_。 +注意这个例子使用了[函数组件](https://reactjs.org/docs/components-and-props.html#functional-and-class-components),我们可以让它更像一点*类*。 ```typescript -import * as React from "react"; +import * as React from 'react'; -export interface HelloProps { compiler: string; framework: string; } +export interface HelloProps { + compiler: string; + framework: string; +} // 'HelloProps' describes the shape of props. // State is never set so we use the '{}' type. export class Hello extends React.Component { - render() { - return

Hello from {this.props.compiler} and {this.props.framework}!

; - } + render() { + return ( +

+ Hello from {this.props.compiler} and {this.props.framework}! +

+ ); + } } ``` 接下来,在`src`下创建`index.tsx`文件,源码如下: ```typescript -import * as React from "react"; -import * as ReactDOM from "react-dom"; +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; -import { Hello } from "./components/Hello"; +import { Hello } from './components/Hello'; ReactDOM.render( - , - document.getElementById("example") + , + document.getElementById('example') ); ``` -我们仅仅将`Hello`组件导入`index.tsx`。 注意,不同于`"react"`或`"react-dom"`,我们使用`Hello.tsx`的_相对路径_ - 这很重要。 如果不这样做,TypeScript只会尝试在`node_modules`文件夹里查找。 +我们仅仅将`Hello`组件导入`index.tsx`。 注意,不同于`"react"`或`"react-dom"`,我们使用`Hello.tsx`的*相对路径* - 这很重要。 如果不这样做,TypeScript 只会尝试在`node_modules`文件夹里查找。 我们还需要一个页面来显示`Hello`组件。 在根目录`proj`创建一个名为`index.html`的文件,如下: @@ -167,60 +181,60 @@ ReactDOM.render( ``` -需要注意一点我们是从`node_modules`引入的文件。 React和React-DOM的npm包里包含了独立的`.js`文件,你可以在页面上引入它们,这里我们为了快捷就直接引用了。 可以随意地将它们拷贝到其它目录下,或者从CDN上引用。 Facebook在CND上提供了一系列可用的React版本,你可以在这里查看[更多内容](http://facebook.github.io/react/downloads.html#development-vs.-production-builds)。 +需要注意一点我们是从`node_modules`引入的文件。 React 和 React-DOM 的 npm 包里包含了独立的`.js`文件,你可以在页面上引入它们,这里我们为了快捷就直接引用了。 可以随意地将它们拷贝到其它目录下,或者从 CDN 上引用。 Facebook 在 CND 上提供了一系列可用的 React 版本,你可以在这里查看[更多内容](http://facebook.github.io/react/downloads.html#development-vs.-production-builds)。 -## 创建一个webpack配置文件 +## 创建一个 webpack 配置文件 在工程根目录下创建一个`webpack.config.js`文件。 ```javascript module.exports = { - mode: "production", - - // Enable sourcemaps for debugging webpack's output. - devtool: "source-map", - - resolve: { - // Add '.ts' and '.tsx' as resolvable extensions. - extensions: [".ts", ".tsx"] - }, - - module: { - rules: [ - { - test: /\.ts(x?)$/, - exclude: /node_modules/, - use: [ - { - loader: "ts-loader" - } - ] - }, - // All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'. - { - enforce: "pre", - test: /\.js$/, - loader: "source-map-loader" - } - ] - }, - - // When importing a module whose path matches one of the following, just - // assume a corresponding global variable exists and use that instead. - // This is important because it allows us to avoid bundling all of our - // dependencies, which allows browsers to cache those libraries between builds. - externals: { - "react": "React", - "react-dom": "ReactDOM" - } + mode: 'production', + + // Enable sourcemaps for debugging webpack's output. + devtool: 'source-map', + + resolve: { + // Add '.ts' and '.tsx' as resolvable extensions. + extensions: ['.ts', '.tsx'], + }, + + module: { + rules: [ + { + test: /\.ts(x?)$/, + exclude: /node_modules/, + use: [ + { + loader: 'ts-loader', + }, + ], + }, + // All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'. + { + enforce: 'pre', + test: /\.js$/, + loader: 'source-map-loader', + }, + ], + }, + + // When importing a module whose path matches one of the following, just + // assume a corresponding global variable exists and use that instead. + // This is important because it allows us to avoid bundling all of our + // dependencies, which allows browsers to cache those libraries between builds. + externals: { + react: 'React', + 'react-dom': 'ReactDOM', + }, }; ``` -大家可能对`externals`字段有所疑惑。 我们想要避免把所有的React都放到一个文件里,因为会增加编译时间并且浏览器还能够缓存没有发生改变的库文件。 +大家可能对`externals`字段有所疑惑。 我们想要避免把所有的 React 都放到一个文件里,因为会增加编译时间并且浏览器还能够缓存没有发生改变的库文件。 -理想情况下,我们只需要在浏览器里引入React模块,但是大部分浏览器还没有支持模块。 因此大部分代码库会把自己包裹在一个单独的全局变量内,比如:`jQuery`或`_`。 这叫做“命名空间”模式,webpack也允许我们继续使用通过这种方式写的代码库。 通过我们的设置`"react": "React"`,webpack会神奇地将所有对`"react"`的导入转换成从`React`全局变量中加载。 +理想情况下,我们只需要在浏览器里引入 React 模块,但是大部分浏览器还没有支持模块。 因此大部分代码库会把自己包裹在一个单独的全局变量内,比如:`jQuery`或`_`。 这叫做“命名空间”模式,webpack 也允许我们继续使用通过这种方式写的代码库。 通过我们的设置`"react": "React"`,webpack 会神奇地将所有对`"react"`的导入转换成从`React`全局变量中加载。 -你可以在[这里](https://webpack.js.org/concepts)了解更多如何配置webpack。 +你可以在[这里](https://webpack.js.org/concepts)了解更多如何配置 webpack。 ## 整合在一起 @@ -231,4 +245,3 @@ npx webpack ``` 在浏览器里打开`index.html`,工程应该已经可以用了! 你可以看到页面上显示着“Hello from TypeScript and React!” - diff --git a/zh/tutorials/react.md b/zh/tutorials/react.md index 2ab5b74c..e88d850e 100644 --- a/zh/tutorials/react.md +++ b/zh/tutorials/react.md @@ -1,15 +1,15 @@ # React -这篇快速上手指南会教你如何将TypeScript与[React](https://reactjs.org/)结合起来使用。 在最后,你将学到: +这篇快速上手指南会教你如何将 TypeScript 与[React](https://reactjs.org/)结合起来使用。 在最后,你将学到: -* 使用TypeScript和React创建工程 -* 使用[TSLint](https://github.com/palantir/tslint)进行代码检查 -* 使用[Jest](https://facebook.github.io/jest/)和[Enzyme](http://airbnb.io/enzyme/)进行测试,以及 -* 使用[Redux](https://github.com/reactjs/react-redux)管理状态 +- 使用 TypeScript 和 React 创建工程 +- 使用[TSLint](https://github.com/palantir/tslint)进行代码检查 +- 使用[Jest](https://facebook.github.io/jest/)和[Enzyme](http://airbnb.io/enzyme/)进行测试,以及 +- 使用[Redux](https://github.com/reactjs/react-redux)管理状态 我们会使用[create-react-app](https://github.com/facebookincubator/create-react-app)工具快速搭建工程环境。 -这里假设你已经在使用[Node.js](https://nodejs.org/)和[npm](https://www.npmjs.com/)。 并且已经了解了[React的基础知识](https://reactjs.org/docs/hello-world.html)。 +这里假设你已经在使用[Node.js](https://nodejs.org/)和[npm](https://www.npmjs.com/)。 并且已经了解了[React 的基础知识](https://reactjs.org/docs/hello-world.html)。 ## 创建新工程 @@ -19,7 +19,7 @@ npx create-react-app my-app --template typescript ``` -[react-scripts-ts](https://www.npmjs.com/package/react-scripts-ts)是一系列适配器,它利用标准的create-react-app工程管道并把TypeScript混入进来。 +[react-scripts-ts](https://www.npmjs.com/package/react-scripts-ts)是一系列适配器,它利用标准的 create-react-app 工程管道并把 TypeScript 混入进来。 此时的工程结构应如下所示: @@ -37,11 +37,11 @@ my-app/ 注意: -* `tsconfig.json`包含了工程里TypeScript特定的选项。 -* `tslint.json`保存了要使用的代码检查器的设置,[TSLint](https://github.com/palantir/tslint)。 -* `package.json`包含了依赖,还有一些命令的快捷方式,如测试命令,预览命令和发布应用的命令。 -* `public`包含了静态资源如HTML页面或图片。除了`index.html`文件外,其它的文件都可以删除。 -* `src`包含了TypeScript和CSS源码。`index.tsx`是强制使用的入口文件。 +- `tsconfig.json`包含了工程里 TypeScript 特定的选项。 +- `tslint.json`保存了要使用的代码检查器的设置,[TSLint](https://github.com/palantir/tslint)。 +- `package.json`包含了依赖,还有一些命令的快捷方式,如测试命令,预览命令和发布应用的命令。 +- `public`包含了静态资源如 HTML 页面或图片。除了`index.html`文件外,其它的文件都可以删除。 +- `src`包含了 TypeScript 和 CSS 源码。`index.tsx`是强制使用的入口文件。 ## 运行工程 @@ -63,7 +63,7 @@ npm run start npm run test ``` -这个命令会运行Jest,一个非常好用的测试工具,它会运行所有扩展名是`.test.ts`或`.spec.ts`的文件。 好比是`npm run start`命令,当检测到有改动的时候Jest会自动地运行。 如果喜欢的话,你还可以同时运行`npm run start`和`npm run test`,这样你就可以在预览的同时进行测试。 +这个命令会运行 Jest,一个非常好用的测试工具,它会运行所有扩展名是`.test.ts`或`.spec.ts`的文件。 好比是`npm run start`命令,当检测到有改动的时候 Jest 会自动地运行。 如果喜欢的话,你还可以同时运行`npm run start`和`npm run test`,这样你就可以在预览的同时进行测试。 ## 生成生产环境的构建版本 @@ -75,7 +75,7 @@ npm run test npm run build ``` -这会相应地创建优化过的JS和CSS文件,`./build/static/js`和`./build/static/css`。 +这会相应地创建优化过的 JS 和 CSS 文件,`./build/static/js`和`./build/static/css`。 大多数情况下你不需要生成生产环境的构建版本, 但它可以帮助你衡量应用最终版本的体积大小。 @@ -124,7 +124,7 @@ function getExclamationMarks(numChars: number) { 我们创建了一个函数组件`Hello`。 具体来讲,`Hello`是一个函数,接收一个`Props`对象并拆解它。 如果`Props`对象里没有设置`enthusiasmLevel`,默认值为`1`。 -使用函数是React中定义组件的[两种方式](https://reactjs.org/docs/components-and-props.html#functional-and-class-components)之一。 如果你喜欢的话,也_可以_通过类的方式定义: +使用函数是 React 中定义组件的[两种方式](https://reactjs.org/docs/components-and-props.html#functional-and-class-components)之一。 如果你喜欢的话,也*可以*通过类的方式定义: ```typescript class Hello extends React.Component { @@ -146,7 +146,7 @@ class Hello extends React.Component { } ``` -当我们的[组件具有某些状态](https://reactjs.org/docs/state-and-lifecycle.html)的时候,使用类的方式是很有用处的。 但在这个例子里我们不需要考虑状态 - 事实上,在`React.Component`我们把状态指定为了`object`,因此使用函数组件更简洁。 当在创建可重用的通用UI组件的时候,在表现层使用组件局部状态比较适合。 针对我们应用的生命周期,我们会审视应用是如何通过Redux轻松地管理普通状态的。 +当我们的[组件具有某些状态](https://reactjs.org/docs/state-and-lifecycle.html)的时候,使用类的方式是很有用处的。 但在这个例子里我们不需要考虑状态 - 事实上,在`React.Component`我们把状态指定为了`object`,因此使用函数组件更简洁。 当在创建可重用的通用 UI 组件的时候,在表现层使用组件局部状态比较适合。 针对我们应用的生命周期,我们会审视应用是如何通过 Redux 轻松地管理普通状态的。 现在我们已经写好了组件,让我们仔细看看`index.tsx`,把``替换成``。 @@ -167,41 +167,41 @@ ReactDOM.render( ### 类型断言 -这里还有一点要指出,就是最后一行`document.getElementById('root') as HTMLElement`。 这个语法叫做_类型断言_,有时也叫做_转换_。 当你比类型检查器更清楚一个表达式的类型的时候,你可以通过这种方式通知TypeScript。 +这里还有一点要指出,就是最后一行`document.getElementById('root') as HTMLElement`。 这个语法叫做*类型断言*,有时也叫做*转换*。 当你比类型检查器更清楚一个表达式的类型的时候,你可以通过这种方式通知 TypeScript。 -这里,我们之所以这么做是因为`getElementById`的返回值类型是`HTMLElement | null`。 简单地说,`getElementById`返回`null`是当无法找对对应`id`元素的时候。 我们假设`getElementById`总是成功的,因此我们要使用`as`语法告诉TypeScript这点。 +这里,我们之所以这么做是因为`getElementById`的返回值类型是`HTMLElement | null`。 简单地说,`getElementById`返回`null`是当无法找对对应`id`元素的时候。 我们假设`getElementById`总是成功的,因此我们要使用`as`语法告诉 TypeScript 这点。 -TypeScript还有一种感叹号(`!`)结尾的语法,它会从前面的表达式里移除`null`和`undefined`。 所以我们也_可以_写成`document.getElementById('root')!`,但在这里我们想写的更清楚些。 +TypeScript 还有一种感叹号(`!`)结尾的语法,它会从前面的表达式里移除`null`和`undefined`。 所以我们也*可以*写成`document.getElementById('root')!`,但在这里我们想写的更清楚些。 ## :sunglasses:添加样式 -通过我们的设置为一个组件添加样式很容易。 若要设置`Hello`组件的样式,我们可以创建这样一个CSS文件`src/components/Hello.css`。 +通过我们的设置为一个组件添加样式很容易。 若要设置`Hello`组件的样式,我们可以创建这样一个 CSS 文件`src/components/Hello.css`。 ```css .hello { text-align: center; margin: 20px; font-size: 48px; - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } .hello button { - margin-left: 25px; - margin-right: 25px; - font-size: 40px; - min-width: 50px; + margin-left: 25px; + margin-right: 25px; + font-size: 40px; + min-width: 50px; } ``` -`create-react-app`包含的工具(Webpack和一些加载器)允许我们导入样式表文件。 当我们构建应用的时候,所有导入的`.css`文件会被拼接成一个输出文件。 因此在`src/components/Hello.tsx`,我们需要添加如下导入语句。 +`create-react-app`包含的工具(Webpack 和一些加载器)允许我们导入样式表文件。 当我们构建应用的时候,所有导入的`.css`文件会被拼接成一个输出文件。 因此在`src/components/Hello.tsx`,我们需要添加如下导入语句。 ```typescript import './Hello.css'; ``` -## 使用Jest编写测试 +## 使用 Jest 编写测试 -如果你没使用过Jest,你可能先要把它安装为开发依赖项。 +如果你没使用过 Jest,你可能先要把它安装为开发依赖项。 ```bash npm install -D jest jest-cli jest-config @@ -209,13 +209,13 @@ npm install -D jest jest-cli jest-config 我们对`Hello`组件有一些假设。 让我们在此重申一下: -> * 当这样写``时,组件应被渲染成`
Hello Daniel!!!
`。 -> * 若未指定`enthusiasmLevel`,组件应默认显示一个感叹号。 -> * 若`enthusiasmLevel`为`0`或负值,它应抛出一个错误。 +> - 当这样写``时,组件应被渲染成`
Hello Daniel!!!
`。 +> - 若未指定`enthusiasmLevel`,组件应默认显示一个感叹号。 +> - 若`enthusiasmLevel`为`0`或负值,它应抛出一个错误。 我们将针对这些需求为组件写一些注释。 -但首先,我们要安装Enzyme。 [Enzyme](http://airbnb.io/enzyme/)是React生态系统里一个通用工具,它方便了针对组件的行为编写测试。 默认地,我们的应用包含了一个叫做jsdom的库,它允许我们模拟DOM以及在非浏览器的环境下测试运行时的行为。 Enzyme与此类似,但是是基于jsdom的,并且方便我们查询组件。 +但首先,我们要安装 Enzyme。 [Enzyme](http://airbnb.io/enzyme/)是 React 生态系统里一个通用工具,它方便了针对组件的行为编写测试。 默认地,我们的应用包含了一个叫做 jsdom 的库,它允许我们模拟 DOM 以及在非浏览器的环境下测试运行时的行为。 Enzyme 与此类似,但是是基于 jsdom 的,并且方便我们查询组件。 让我们把它安装为开发依赖项。 @@ -223,17 +223,17 @@ npm install -D jest jest-cli jest-config npm install -D enzyme @types/enzyme enzyme-adapter-react-16 @types/enzyme-adapter-react-16 ``` -如果你的react版本低于15.5.0,还需安装如下 +如果你的 react 版本低于 15.5.0,还需安装如下 ```bash npm install -D react-addons-test-utils ``` -注意我们同时安装了`enzyme`和`@types/enzyme`。 `enzyme`包指的是包含了实际运行的JavaScript代码包,而`@types/enzyme`则包含了声明文件(`.d.ts`文件)的包,以便TypeScript能够了解该如何使用Enzyme。 你可以在[这里](https://www.typescriptlang.org/docs/handbook/declaration-files/consumption.html)了解更多关于`@types`包的信息。 +注意我们同时安装了`enzyme`和`@types/enzyme`。 `enzyme`包指的是包含了实际运行的 JavaScript 代码包,而`@types/enzyme`则包含了声明文件(`.d.ts`文件)的包,以便 TypeScript 能够了解该如何使用 Enzyme。 你可以在[这里](https://www.typescriptlang.org/docs/handbook/declaration-files/consumption.html)了解更多关于`@types`包的信息。 -我们还需要安装`enzyme-adapter`和`react-addons-test-utils`。 它们是使用`enzyme`所需要安装的包,前者作为配置适配器是必须的,而后者若采用的React版本在15.5.0之上则毋需安装。 +我们还需要安装`enzyme-adapter`和`react-addons-test-utils`。 它们是使用`enzyme`所需要安装的包,前者作为配置适配器是必须的,而后者若采用的 React 版本在 15.5.0 之上则毋需安装。 -现在我们已经设置好了Enzyme,下面开始编写测试! 先创建一个文件`src/components/Hello.test.tsx`,与先前的`Hello.tsx`文件放在一起。 +现在我们已经设置好了 Enzyme,下面开始编写测试! 先创建一个文件`src/components/Hello.test.tsx`,与先前的`Hello.tsx`文件放在一起。 ```typescript // src/components/Hello.test.tsx @@ -246,60 +246,60 @@ import Hello from './Hello'; enzyme.configure({ adapter: new Adapter() }); it('renders the correct text when no enthusiasm level is given', () => { - const hello = enzyme.shallow(); - expect(hello.find(".greeting").text()).toEqual('Hello Daniel!') + const hello = enzyme.shallow(); + expect(hello.find('.greeting').text()).toEqual('Hello Daniel!'); }); it('renders the correct text with an explicit enthusiasm of 1', () => { - const hello = enzyme.shallow(); - expect(hello.find(".greeting").text()).toEqual('Hello Daniel!') + const hello = enzyme.shallow(); + expect(hello.find('.greeting').text()).toEqual('Hello Daniel!'); }); it('renders the correct text with an explicit enthusiasm level of 5', () => { - const hello = enzyme.shallow(); - expect(hello.find(".greeting").text()).toEqual('Hello Daniel!!!!!'); + const hello = enzyme.shallow(); + expect(hello.find('.greeting').text()).toEqual('Hello Daniel!!!!!'); }); it('throws when the enthusiasm level is 0', () => { expect(() => { - enzyme.shallow(); + enzyme.shallow(); }).toThrow(); }); it('throws when the enthusiasm level is negative', () => { expect(() => { - enzyme.shallow(); + enzyme.shallow(); }).toThrow(); }); ``` 这些测试都十分基础,但你可以从中得到启发。 -## 添加state管理 +## 添加 state 管理 -到此为止,如果你使用React的目的是只获取一次数据并显示,那么你已经完成了。 但是如果你想开发一个可以交互的应用,那么你需要添加state管理。 +到此为止,如果你使用 React 的目的是只获取一次数据并显示,那么你已经完成了。 但是如果你想开发一个可以交互的应用,那么你需要添加 state 管理。 -### state管理概述 +### state 管理概述 -React本身就是一个适合于创建可组合型视图的库。 但是,React并没有任何在应用间同步数据的功能。 就React组件而言,数据是通过每个元素上指定的props向子元素传递。 +React 本身就是一个适合于创建可组合型视图的库。 但是,React 并没有任何在应用间同步数据的功能。 就 React 组件而言,数据是通过每个元素上指定的 props 向子元素传递。 -因为React本身并没有提供内置的state管理功能,React社区选择了Redux和MobX库。 +因为 React 本身并没有提供内置的 state 管理功能,React 社区选择了 Redux 和 MobX 库。 -[Redux](http://redux.js.org)依靠一个统一且不可变的数据存储来同步数据,并且更新那里的数据时会触发应用的更新渲染。 state的更新是以一种不可变的方式进行,它会发布一条明确的action消息,这个消息必须被reducer函数处理。 由于使用了这样明确的方式,很容易弄清楚一个action是如何影响程序的state。 +[Redux](http://redux.js.org)依靠一个统一且不可变的数据存储来同步数据,并且更新那里的数据时会触发应用的更新渲染。 state 的更新是以一种不可变的方式进行,它会发布一条明确的 action 消息,这个消息必须被 reducer 函数处理。 由于使用了这样明确的方式,很容易弄清楚一个 action 是如何影响程序的 state。 -[MobX](https://mobx.js.org/)借助于函数式响应型模式,state被包装在了可观察对象里,并通过props传递。 通过将state标记为可观察的,即可在所有观察者之间保持state的同步性。 另一个好处是,这个库已经使用TypeScript实现了。 +[MobX](https://mobx.js.org/)借助于函数式响应型模式,state 被包装在了可观察对象里,并通过 props 传递。 通过将 state 标记为可观察的,即可在所有观察者之间保持 state 的同步性。 另一个好处是,这个库已经使用 TypeScript 实现了。 -这两者各有优缺点。 但Redux使用得更广泛,因此在这篇教程里,我们主要看如何使用Redux; 但是也鼓励大家两者都去了解一下。 +这两者各有优缺点。 但 Redux 使用得更广泛,因此在这篇教程里,我们主要看如何使用 Redux; 但是也鼓励大家两者都去了解一下。 -后面的小节学习曲线比较陡。 因此强烈建议大家先去[熟悉一下Redux](http://redux.js.org/)。 +后面的小节学习曲线比较陡。 因此强烈建议大家先去[熟悉一下 Redux](http://redux.js.org/)。 -### 设置actions +### 设置 actions -只有当应用里的state会改变的时候,我们才需要去添加Redux。 我们需要一个action的来源,它将触发改变。 它可以是一个定时器或者UI上的一个按钮。 +只有当应用里的 state 会改变的时候,我们才需要去添加 Redux。 我们需要一个 action 的来源,它将触发改变。 它可以是一个定时器或者 UI 上的一个按钮。 为此,我们将增加两个按钮来控制`Hello`组件的感叹级别。 -### 安装Redux +### 安装 Redux 安装`redux`和`react-redux`以及它们的类型文件做为依赖。 @@ -307,24 +307,24 @@ React本身就是一个适合于创建可组合型视图的库。 但是,React npm install -S redux react-redux @types/react-redux ``` -这里我们不需要安装`@types/redux`,因为Redux已经自带了声明文件(`.d.ts`文件)。 +这里我们不需要安装`@types/redux`,因为 Redux 已经自带了声明文件(`.d.ts`文件)。 ### 定义应用的状态 -我们需要定义Redux保存的state的结构。 创建`src/types/index.tsx`文件,它保存了类型的定义,我们在整个程序里都可能用到。 +我们需要定义 Redux 保存的 state 的结构。 创建`src/types/index.tsx`文件,它保存了类型的定义,我们在整个程序里都可能用到。 ```typescript // src/types/index.tsx export interface StoreState { - languageName: string; - enthusiasmLevel: number; + languageName: string; + enthusiasmLevel: number; } ``` -这里我们想让`languageName`表示应用使用的编程语言(例如,TypeScript或者JavaScript),`enthusiasmLevel`是可变的。 在写我们的第一个容器的时候,就会明白为什么要令state与props稍有不同。 +这里我们想让`languageName`表示应用使用的编程语言(例如,TypeScript 或者 JavaScript),`enthusiasmLevel`是可变的。 在写我们的第一个容器的时候,就会明白为什么要令 state 与 props 稍有不同。 -### 添加actions +### 添加 actions 下面我们创建这个应用将要响应的消息类型,`src/constants/index.tsx`。 @@ -334,50 +334,49 @@ export interface StoreState { export const INCREMENT_ENTHUSIASM = 'INCREMENT_ENTHUSIASM'; export type INCREMENT_ENTHUSIASM = typeof INCREMENT_ENTHUSIASM; - export const DECREMENT_ENTHUSIASM = 'DECREMENT_ENTHUSIASM'; export type DECREMENT_ENTHUSIASM = typeof DECREMENT_ENTHUSIASM; ``` -这里的`const`/`type`模式允许我们以容易访问和重构的方式使用TypeScript的字符串字面量类型。 +这里的`const`/`type`模式允许我们以容易访问和重构的方式使用 TypeScript 的字符串字面量类型。 -接下来,我们创建一些actions以及创建这些actions的函数,`src/actions/index.tsx`。 +接下来,我们创建一些 actions 以及创建这些 actions 的函数,`src/actions/index.tsx`。 ```typescript -import * as constants from '../constants' +import * as constants from '../constants'; export interface IncrementEnthusiasm { - type: constants.INCREMENT_ENTHUSIASM; + type: constants.INCREMENT_ENTHUSIASM; } export interface DecrementEnthusiasm { - type: constants.DECREMENT_ENTHUSIASM; + type: constants.DECREMENT_ENTHUSIASM; } export type EnthusiasmAction = IncrementEnthusiasm | DecrementEnthusiasm; export function incrementEnthusiasm(): IncrementEnthusiasm { - return { - type: constants.INCREMENT_ENTHUSIASM - } + return { + type: constants.INCREMENT_ENTHUSIASM, + }; } export function decrementEnthusiasm(): DecrementEnthusiasm { - return { - type: constants.DECREMENT_ENTHUSIASM - } + return { + type: constants.DECREMENT_ENTHUSIASM, + }; } ``` -我们创建了两个类型,它们负责增加操作和减少操作的行为。 我们还定义了一个类型(`EnthusiasmAction`),它描述了哪些action是可以增加或减少的。 最后,我们定义了两个函数用来创建实际的actions。 +我们创建了两个类型,它们负责增加操作和减少操作的行为。 我们还定义了一个类型(`EnthusiasmAction`),它描述了哪些 action 是可以增加或减少的。 最后,我们定义了两个函数用来创建实际的 actions。 这里有一些清晰的模版,你可以参考类似[redux-actions](https://www.npmjs.com/package/redux-actions)的库。 -### 添加reducer +### 添加 reducer -现在我们可以开始写第一个reducer了! Reducers是函数,它们负责生成应用state的拷贝使之产生变化,但它并没有_副作用_。 它们是一种[_纯函数_](https://en.wikipedia.org/wiki/Pure_function)。 +现在我们可以开始写第一个 reducer 了! Reducers 是函数,它们负责生成应用 state 的拷贝使之产生变化,但它并没有*副作用*。 它们是一种[_纯函数_](https://en.wikipedia.org/wiki/Pure_function)。 -我们的reducer将放在`src/reducers/index.tsx`文件里。 它的功能是保证增加操作会让感叹级别加1,减少操作则要将感叹级别减1,但是这个级别永远不能小于1。 +我们的 reducer 将放在`src/reducers/index.tsx`文件里。 它的功能是保证增加操作会让感叹级别加 1,减少操作则要将感叹级别减 1,但是这个级别永远不能小于 1。 ```typescript // src/reducers/index.tsx @@ -386,24 +385,30 @@ import { EnthusiasmAction } from '../actions'; import { StoreState } from '../types/index'; import { INCREMENT_ENTHUSIASM, DECREMENT_ENTHUSIASM } from '../constants/index'; -export function enthusiasm(state: StoreState, action: EnthusiasmAction): StoreState { +export function enthusiasm( + state: StoreState, + action: EnthusiasmAction +): StoreState { switch (action.type) { case INCREMENT_ENTHUSIASM: return { ...state, enthusiasmLevel: state.enthusiasmLevel + 1 }; case DECREMENT_ENTHUSIASM: - return { ...state, enthusiasmLevel: Math.max(1, state.enthusiasmLevel - 1) }; + return { + ...state, + enthusiasmLevel: Math.max(1, state.enthusiasmLevel - 1), + }; } return state; } ``` -注意我们使用了_对象展开_(`...state`),当替换`enthusiasmLevel`时,它可以对状态进行浅拷贝。 将`enthusiasmLevel`属性放在末尾是十分关键的,否则它将被旧的状态覆盖。 +注意我们使用了*对象展开*(`...state`),当替换`enthusiasmLevel`时,它可以对状态进行浅拷贝。 将`enthusiasmLevel`属性放在末尾是十分关键的,否则它将被旧的状态覆盖。 -你可能想要对reducer写一些测试。 因为reducers是纯函数,它们可以传入任意的数据。 针对每个输入,可以测试reducers生成的新的状态。 可以考虑使用Jest的[toEqual](https://facebook.github.io/jest/docs/en/expect.html#toequalvalue)方法。 +你可能想要对 reducer 写一些测试。 因为 reducers 是纯函数,它们可以传入任意的数据。 针对每个输入,可以测试 reducers 生成的新的状态。 可以考虑使用 Jest 的[toEqual](https://facebook.github.io/jest/docs/en/expect.html#toequalvalue)方法。 ### 创建容器 -在使用Redux时,我们常常要创建组件和容器。 组件是数据无关的,且工作在表现层。 _容器_通常包裹组件及其使用的数据,用以显示和修改状态。 你可以在这里阅读更多关于这个概念的细节:[Dan Abramov写的_表现层的容器组件_](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0)。 +在使用 Redux 时,我们常常要创建组件和容器。 组件是数据无关的,且工作在表现层。 *容器*通常包裹组件及其使用的数据,用以显示和修改状态。 你可以在这里阅读更多关于这个概念的细节:[Dan Abramov 写的*表现层的容器组件*](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0)。 现在我们修改`src/components/Hello.tsx`,让它可以修改状态。 我们将添加两个可选的回调属性到`Props`,它们分别是`onIncrement`和`onDecrement`: @@ -449,30 +454,32 @@ import { StoreState } from '../types/index'; import { connect, Dispatch } from 'react-redux'; ``` -两个关键点是初始的`Hello`组件和react-redux的`connect`函数。 `connect`可以将我们的`Hello`组件转换成一个容器,通过以下两个函数: +两个关键点是初始的`Hello`组件和 react-redux 的`connect`函数。 `connect`可以将我们的`Hello`组件转换成一个容器,通过以下两个函数: -* `mapStateToProps`将当前store里的数据以我们的组件需要的形式传递到组件。 -* `mapDispatchToProps`利用`dispatch`函数,创建回调props将actions送到store。 +- `mapStateToProps`将当前 store 里的数据以我们的组件需要的形式传递到组件。 +- `mapDispatchToProps`利用`dispatch`函数,创建回调 props 将 actions 送到 store。 -回想一下,我们的应用包含两个属性:`languageName`和`enthusiasmLevel`。 我们的`Hello`组件,希望得到一个`name`和一个`enthusiasmLevel`。 `mapStateToProps`会从store得到相应的数据,如果需要的话将针对组件的props调整它。 下面让我们继续往下写。 +回想一下,我们的应用包含两个属性:`languageName`和`enthusiasmLevel`。 我们的`Hello`组件,希望得到一个`name`和一个`enthusiasmLevel`。 `mapStateToProps`会从 store 得到相应的数据,如果需要的话将针对组件的 props 调整它。 下面让我们继续往下写。 ```typescript export function mapStateToProps({ enthusiasmLevel, languageName }: StoreState) { return { enthusiasmLevel, name: languageName, - } + }; } ``` -注意`mapStateToProps`仅创建了`Hello`组件需要的四个属性中的两个。 我们还想要传入`onIncrement`和`onDecrement`回调函数。 `mapDispatchToProps`是一个函数,它需要传入一个调度函数。 这个调度函数可以将actions传入store来触发更新,因此我们可以创建一对回调函数,它们会在需要的时候调用调度函数。 +注意`mapStateToProps`仅创建了`Hello`组件需要的四个属性中的两个。 我们还想要传入`onIncrement`和`onDecrement`回调函数。 `mapDispatchToProps`是一个函数,它需要传入一个调度函数。 这个调度函数可以将 actions 传入 store 来触发更新,因此我们可以创建一对回调函数,它们会在需要的时候调用调度函数。 ```typescript -export function mapDispatchToProps(dispatch: Dispatch) { +export function mapDispatchToProps( + dispatch: Dispatch +) { return { onIncrement: () => dispatch(actions.incrementEnthusiasm()), onDecrement: () => dispatch(actions.decrementEnthusiasm()), - } + }; } ``` @@ -496,22 +503,24 @@ export function mapStateToProps({ enthusiasmLevel, languageName }: StoreState) { return { enthusiasmLevel, name: languageName, - } + }; } -export function mapDispatchToProps(dispatch: Dispatch) { +export function mapDispatchToProps( + dispatch: Dispatch +) { return { onIncrement: () => dispatch(actions.incrementEnthusiasm()), onDecrement: () => dispatch(actions.decrementEnthusiasm()), - } + }; } export default connect(mapStateToProps, mapDispatchToProps)(Hello); ``` -### 创建store +### 创建 store -让我们回到`src/index.tsx`。 要把所有的东西合到一起,我们需要创建一个带初始状态的store,并用我们所有的reducers来设置它。 +让我们回到`src/index.tsx`。 要把所有的东西合到一起,我们需要创建一个带初始状态的 store,并用我们所有的 reducers 来设置它。 ```typescript import { createStore } from 'redux'; @@ -524,9 +533,9 @@ const store = createStore(enthusiasm, { }); ``` -`store`可能正如你想的那样,它是我们应用全局状态的核心store。 +`store`可能正如你想的那样,它是我们应用全局状态的核心 store。 -接下来,我们将要用`./src/containers/Hello`来包裹`./src/components/Hello`,然后使用react-redux的`Provider`将props与容器连通起来。 我们将导入它们: +接下来,我们将要用`./src/containers/Hello`来包裹`./src/components/Hello`,然后使用 react-redux 的`Provider`将 props 与容器连通起来。 我们将导入它们: ```typescript import Hello from './containers/Hello'; @@ -544,11 +553,11 @@ ReactDOM.render( ); ``` -注意,`Hello`不再需要props了,因为我们使用了`connect`函数为包裹起来的`Hello`组件的props适配了应用的状态。 +注意,`Hello`不再需要 props 了,因为我们使用了`connect`函数为包裹起来的`Hello`组件的 props 适配了应用的状态。 ## 退出 -如果你发现create-react-app使一些自定义设置变得困难,那么你就可以选择不使用它,使用你需要配置。 比如,你要添加一个Webpack插件,你就可以利用create-react-app提供的“eject”功能。 +如果你发现 create-react-app 使一些自定义设置变得困难,那么你就可以选择不使用它,使用你需要配置。 比如,你要添加一个 Webpack 插件,你就可以利用 create-react-app 提供的“eject”功能。 运行: @@ -558,15 +567,14 @@ npm run eject 这样就可以了! -你要注意,在运行eject前最好保存你的代码。 你不能撤销eject命令,因此退出操作是永久性的除非你从一个运行eject前的提交来恢复工程。 +你要注意,在运行 eject 前最好保存你的代码。 你不能撤销 eject 命令,因此退出操作是永久性的除非你从一个运行 eject 前的提交来恢复工程。 ## 下一步 -create-react-app带有很多很棒的功能。 它们的大多数都在我们工程生成的`README.md`里面有记录,所以可以简单阅读一下。 - -如果你想学习更多关于Redux的知识,你可以前往[官方站点](http://redux.js.org/)查看文档。 同样的,[MobX](https://mobx.js.org/)官方站点。 +create-react-app 带有很多很棒的功能。 它们的大多数都在我们工程生成的`README.md`里面有记录,所以可以简单阅读一下。 -如果你想要在某个时间点eject,你需要了解再多关于Webpack的知识。 你可以查看[React & Webpack教程](react-and-webpack.md)。 +如果你想学习更多关于 Redux 的知识,你可以前往[官方站点](http://redux.js.org/)查看文档。 同样的,[MobX](https://mobx.js.org/)官方站点。 -有时候你需要路由功能。 已经有一些解决方案了,但是对于Redux工程来讲[react-router](https://github.com/ReactTraining/react-router)是最流行的,并经常与[react-router-redux](https://github.com/reactjs/react-router-redux)联合使用。 +如果你想要在某个时间点 eject,你需要了解再多关于 Webpack 的知识。 你可以查看[React & Webpack 教程](react-and-webpack.md)。 +有时候你需要路由功能。 已经有一些解决方案了,但是对于 Redux 工程来讲[react-router](https://github.com/ReactTraining/react-router)是最流行的,并经常与[react-router-redux](https://github.com/reactjs/react-router-redux)联合使用。 diff --git a/zh/tutorials/typescript-in-5-minutes.md b/zh/tutorials/typescript-in-5-minutes.md index 470f4526..d6cd14e8 100644 --- a/zh/tutorials/typescript-in-5-minutes.md +++ b/zh/tutorials/typescript-in-5-minutes.md @@ -1,67 +1,67 @@ -# 5分钟了解TypeScript +# 5 分钟了解 TypeScript -让我们使用TypeScript来创建一个简单的Web应用。 +让我们使用 TypeScript 来创建一个简单的 Web 应用。 -## 安装TypeScript +## 安装 TypeScript -有两种主要的方式来获取TypeScript工具: +有两种主要的方式来获取 TypeScript 工具: -* 通过npm(Node.js包管理器) -* 安装Visual Studio的TypeScript插件 +- 通过 npm(Node.js 包管理器) +- 安装 Visual Studio 的 TypeScript 插件 -Visual Studio 2017和Visual Studio 2015 Update 3默认包含了TypeScript。 如果你的Visual Studio还没有安装TypeScript,你可以下载它。 +Visual Studio 2017 和 Visual Studio 2015 Update 3 默认包含了 TypeScript。 如果你的 Visual Studio 还没有安装 TypeScript,你可以下载它。 -针对使用npm的用户: +针对使用 npm 的用户: ```text > npm install -g typescript ``` -## 构建你的第一个TypeScript文件 +## 构建你的第一个 TypeScript 文件 在编辑器,将下面的代码输入到`greeter.ts`文件里: ```typescript function greeter(person) { - return "Hello, " + person; + return 'Hello, ' + person; } -let user = "Jane User"; +let user = 'Jane User'; document.body.textContent = greeter(user); ``` ## 编译代码 -我们使用了`.ts`扩展名,但是这段代码仅仅是JavaScript而已。 你可以直接从现有的JavaScript应用里复制/粘贴这段代码。 +我们使用了`.ts`扩展名,但是这段代码仅仅是 JavaScript 而已。 你可以直接从现有的 JavaScript 应用里复制/粘贴这段代码。 -在命令行上,运行TypeScript编译器: +在命令行上,运行 TypeScript 编译器: ```text tsc greeter.ts ``` -输出结果为一个`greeter.js`文件,它包含了和输入文件中相同的JavsScript代码。 一切准备就绪,我们可以运行这个使用TypeScript写的JavaScript应用了! +输出结果为一个`greeter.js`文件,它包含了和输入文件中相同的 JavsScript 代码。 一切准备就绪,我们可以运行这个使用 TypeScript 写的 JavaScript 应用了! -接下来让我们看看TypeScript工具带来的高级功能。 给`person`函数的参数添加`: string`类型注解,如下: +接下来让我们看看 TypeScript 工具带来的高级功能。 给`person`函数的参数添加`: string`类型注解,如下: ```typescript function greeter(person: string) { - return "Hello, " + person; + return 'Hello, ' + person; } -let user = "Jane User"; +let user = 'Jane User'; document.body.textContent = greeter(user); ``` ## 类型注解 -TypeScript里的类型注解是一种轻量级的为函数或变量添加约束的方式。 在这个例子里,我们希望`greeter`函数接收一个字符串参数。 然后尝试把`greeter`的调用改成传入一个数组: +TypeScript 里的类型注解是一种轻量级的为函数或变量添加约束的方式。 在这个例子里,我们希望`greeter`函数接收一个字符串参数。 然后尝试把`greeter`的调用改成传入一个数组: ```typescript function greeter(person: string) { - return "Hello, " + person; + return 'Hello, ' + person; } let user = [0, 1, 2]; @@ -75,32 +75,32 @@ document.body.textContent = greeter(user); error TS2345: Argument of type 'number[]' is not assignable to parameter of type 'string'. ``` -类似地,尝试删除`greeter`调用的所有参数。 TypeScript会告诉你使用了非期望个数的参数调用了这个函数。 在这两种情况中,TypeScript提供了静态的代码分析,它可以分析代码结构和提供的类型注解。 +类似地,尝试删除`greeter`调用的所有参数。 TypeScript 会告诉你使用了非期望个数的参数调用了这个函数。 在这两种情况中,TypeScript 提供了静态的代码分析,它可以分析代码结构和提供的类型注解。 -要注意的是尽管有错误,`greeter.js`文件还是被创建了。 就算你的代码里有错误,你仍然可以使用TypeScript。但在这种情况下,TypeScript会警告你代码可能不会按预期执行。 +要注意的是尽管有错误,`greeter.js`文件还是被创建了。 就算你的代码里有错误,你仍然可以使用 TypeScript。但在这种情况下,TypeScript 会警告你代码可能不会按预期执行。 ## 接口 -让我们开发这个示例应用。这里我们使用接口来描述一个拥有`firstName`和`lastName`字段的对象。 在TypeScript里,只要两个类型内部的结构兼容那么这两个类型就是兼容的。 这就允许我们在实现接口时候只要保证包含了接口要求的结构就可以,而不必明确地使用`implements`语句。 +让我们开发这个示例应用。这里我们使用接口来描述一个拥有`firstName`和`lastName`字段的对象。 在 TypeScript 里,只要两个类型内部的结构兼容那么这两个类型就是兼容的。 这就允许我们在实现接口时候只要保证包含了接口要求的结构就可以,而不必明确地使用`implements`语句。 ```typescript interface Person { - firstName: string; - lastName: string; + firstName: string; + lastName: string; } function greeter(person: Person) { - return "Hello, " + person.firstName + " " + person.lastName; + return 'Hello, ' + person.firstName + ' ' + person.lastName; } -let user = { firstName: "Jane", lastName: "User" }; +let user = { firstName: 'Jane', lastName: 'User' }; document.body.textContent = greeter(user); ``` ## 类 -最后,让我们使用类来改写这个例子。 TypeScript支持JavaScript的新特性,比如支持基于类的面向对象编程。 +最后,让我们使用类来改写这个例子。 TypeScript 支持 JavaScript 的新特性,比如支持基于类的面向对象编程。 让我们创建一个`Student`类,它带有一个构造函数和一些公共字段。 注意类和接口可以一起工作,程序员可以自行决定抽象的级别。 @@ -108,29 +108,33 @@ document.body.textContent = greeter(user); ```typescript class Student { - fullName: string; - constructor(public firstName: string, public middleInitial: string, public lastName: string) { - this.fullName = firstName + " " + middleInitial + " " + lastName; - } + fullName: string; + constructor( + public firstName: string, + public middleInitial: string, + public lastName: string + ) { + this.fullName = firstName + ' ' + middleInitial + ' ' + lastName; + } } interface Person { - firstName: string; - lastName: string; + firstName: string; + lastName: string; } function greeter(person: Person) { - return "Hello, " + person.firstName + " " + person.lastName; + return 'Hello, ' + person.firstName + ' ' + person.lastName; } -let user = new Student("Jane", "M.", "User"); +let user = new Student('Jane', 'M.', 'User'); document.body.textContent = greeter(user); ``` -重新运行`tsc greeter.ts`,你会看到生成的JavaScript代码和原先的一样。 TypeScript里的类只是JavaScript里常用的基于原型面向对象编程的简写。 +重新运行`tsc greeter.ts`,你会看到生成的 JavaScript 代码和原先的一样。 TypeScript 里的类只是 JavaScript 里常用的基于原型面向对象编程的简写。 -## 运行TypeScript Web应用 +## 运行 TypeScript Web 应用 在`greeter.html`里输入如下内容: @@ -146,7 +150,6 @@ document.body.textContent = greeter(user); 在浏览器里打开`greeter.html`运行这个应用! -可选地:在Visual Studio里打开`greeter.ts`或者把代码复制到TypeScript playground。 将鼠标悬停在标识符上查看它们的类型。 注意在某些情况下它们的类型可以被自动地推断出来。 重新输入一下最后一行代码,看一下自动补全列表和参数列表,它们会根据DOM元素类型而变化。 将光标放在`greeter`函数上,点击F12可以跟踪到它的定义。 还有一点,你可以右键点击标识,使用重构功能来重命名。 - -这些类型信息以及工具可以很好的和JavaScript一起工作。 更多的TypeScript功能演示,请查看本网站的示例部分。 +可选地:在 Visual Studio 里打开`greeter.ts`或者把代码复制到 TypeScript playground。 将鼠标悬停在标识符上查看它们的类型。 注意在某些情况下它们的类型可以被自动地推断出来。 重新输入一下最后一行代码,看一下自动补全列表和参数列表,它们会根据 DOM 元素类型而变化。 将光标放在`greeter`函数上,点击 F12 可以跟踪到它的定义。 还有一点,你可以右键点击标识,使用重构功能来重命名。 +这些类型信息以及工具可以很好的和 JavaScript 一起工作。 更多的 TypeScript 功能演示,请查看本网站的示例部分。 diff --git a/zh/wiki/README.md b/zh/wiki/README.md index db212c92..87d0bb0e 100644 --- a/zh/wiki/README.md +++ b/zh/wiki/README.md @@ -1,10 +1,9 @@ # Wiki -* [TypeScript里的this](this-in-typescript.md) -* [编码规范](coding_guidelines.md) -* [常见编译错误](common-errors.md) -* [支持TypeScript的编辑器](typescript-editor-support.md) -* [结合ASP.NET v5使用TypeScript](using-typescript-with-asp.net-5.md) -* [架构概述](architectural-overview.md) -* [发展路线图](roadmap.md) - +- [TypeScript 里的 this](this-in-typescript.md) +- [编码规范](coding_guidelines.md) +- [常见编译错误](common-errors.md) +- [支持 TypeScript 的编辑器](typescript-editor-support.md) +- [结合 ASP.NET v5 使用 TypeScript](using-typescript-with-asp.net-5.md) +- [架构概述](architectural-overview.md) +- [发展路线图](roadmap.md) diff --git a/zh/wiki/architectural-overview.md b/zh/wiki/architectural-overview.md index 589e4100..ac0b2b10 100644 --- a/zh/wiki/architectural-overview.md +++ b/zh/wiki/architectural-overview.md @@ -4,56 +4,56 @@ ![Architectural overview.](https://raw.githubusercontent.com/wiki/Microsoft/TypeScript/images/architecture.png) -* **核心TypeScript编译器** - * **语法分析器(Parser):** 以一系列原文件开始, 根据语言的语法, 生成抽象语法树(AST) - * **联合器(Binder):** 使用一个`Symbol`将针对相同结构的声明联合在一起(例如:同一个接口或模块的不同声明,或拥有相同名字的函数和模块)。这能帮助类型系统推导出这些具名的声明。 - * **类型解析器与检查器(Type resolver / Checker):** 解析每种类型的构造,检查读写语义并生成适当的诊断信息。 - * **生成器(Emitter):** 从一系列输入文件(.ts和.d.ts)生成输出,它们可以是以下形式之一:JavaScript(.js),声明(.d.ts),或者是source maps(.js.map)。 - * **预处理器(Pre-processor):** “编译上下文”指的是某个“程序”里涉及到的所有文件。上下文的创建是通过检查所有从命令行上传入编译器的文件,按顺序,然后再加入这些文件直接引用的其它文件或通过`import`语句和`/// `标签间接引用的其它文件。 +- **核心 TypeScript 编译器** + - **语法分析器(Parser):** 以一系列原文件开始, 根据语言的语法, 生成抽象语法树(AST) + - **联合器(Binder):** 使用一个`Symbol`将针对相同结构的声明联合在一起(例如:同一个接口或模块的不同声明,或拥有相同名字的函数和模块)。这能帮助类型系统推导出这些具名的声明。 + - **类型解析器与检查器(Type resolver / Checker):** 解析每种类型的构造,检查读写语义并生成适当的诊断信息。 + - **生成器(Emitter):** 从一系列输入文件(.ts 和.d.ts)生成输出,它们可以是以下形式之一:JavaScript(.js),声明(.d.ts),或者是 source maps(.js.map)。 + - **预处理器(Pre-processor):** “编译上下文”指的是某个“程序”里涉及到的所有文件。上下文的创建是通过检查所有从命令行上传入编译器的文件,按顺序,然后再加入这些文件直接引用的其它文件或通过`import`语句和`/// `标签间接引用的其它文件。 -沿着引用图走下来你会发现它是一个有序的源文件列表,它们组成了整个程序。 当解析导出(import)的时候,会优先选择“.ts”文件而不是“.d.ts”文件,以确保处理的是最新的文件。 编译器会进行与Nodejs相似的流程来解析导入,沿着目录链查找与将要导入相匹配的带.ts或.d.ts扩展名的文件。 导入失败不会报error,因为可能已经声明了外部模块。 +沿着引用图走下来你会发现它是一个有序的源文件列表,它们组成了整个程序。 当解析导出(import)的时候,会优先选择“.ts”文件而不是“.d.ts”文件,以确保处理的是最新的文件。 编译器会进行与 Nodejs 相似的流程来解析导入,沿着目录链查找与将要导入相匹配的带.ts 或.d.ts 扩展名的文件。 导入失败不会报 error,因为可能已经声明了外部模块。 -* **独立编译器(tsc):** 批处理编译命令行界面。主要处理针对不同支持的引擎读写文件(比如:Node.js)。 -* **语言服务:** “语言服务”在核心编译器管道上暴露了额外的一层,非常适合类编辑器的应用。 +- **独立编译器(tsc):** 批处理编译命令行界面。主要处理针对不同支持的引擎读写文件(比如:Node.js)。 +- **语言服务:** “语言服务”在核心编译器管道上暴露了额外的一层,非常适合类编辑器的应用。 -语言服务支持一系列典型的编辑器操作比如语句自动补全,函数签名提示,代码格式化和突出高亮,着色等。 基本的重构功能比如重命名,调试接口辅助功能比如验证断点,还有TypeScript特有的功能比如支持增量编译(在命令行上使用`--watch`)。 语言服务是被设计用来有效的处理在一个长期存在的编译上下文中文件随着时间改变的情况;在这样的情况下,语言服务提供了与其它编译器接口不同的角度来处理程序和源文件。 +语言服务支持一系列典型的编辑器操作比如语句自动补全,函数签名提示,代码格式化和突出高亮,着色等。 基本的重构功能比如重命名,调试接口辅助功能比如验证断点,还有 TypeScript 特有的功能比如支持增量编译(在命令行上使用`--watch`)。 语言服务是被设计用来有效的处理在一个长期存在的编译上下文中文件随着时间改变的情况;在这样的情况下,语言服务提供了与其它编译器接口不同的角度来处理程序和源文件。 > 请参考 \[\[Using the Language Service API\]\] 以了解更多详细内容。 ## 数据结构 -* **Node:** 抽象语法树(AST)的基本组成块。通常`Node`表示语言语法里的非终结符;一些终结符保存在语法树里比如标识符和字面量。 -* **SourceFile:** 给定源文件的AST。`SourceFile`本身是一个`Node`;它提供了额外的接口用来访问文件的源码,文件里的引用,文件里的标识符列表和文件里的某个位置与它对应的行号与列号的映射。 -* **Program:** `SourceFile`的集合和一系列编译选项代表一个编译单元。`Program`是类型系统和生成代码的主入口。 -* **Symbol:** 具名的声明。`Symbols`是做为联合的结果而创建。`Symbols`连接了树里的声明节点和其它对同一个实体的声明。`Symbols`是语义系统的基本构建块。 -* **Type:** `Type`是语义系统的其它部分。`Type`可能被命名(比如,类和接口),或匿名(比如,对象类型)。 -* **Signature:** 一共有三种`Signature`类型:调用签名(call),构造签名(construct)和索引签名(index)。 +- **Node:** 抽象语法树(AST)的基本组成块。通常`Node`表示语言语法里的非终结符;一些终结符保存在语法树里比如标识符和字面量。 +- **SourceFile:** 给定源文件的 AST。`SourceFile`本身是一个`Node`;它提供了额外的接口用来访问文件的源码,文件里的引用,文件里的标识符列表和文件里的某个位置与它对应的行号与列号的映射。 +- **Program:** `SourceFile`的集合和一系列编译选项代表一个编译单元。`Program`是类型系统和生成代码的主入口。 +- **Symbol:** 具名的声明。`Symbols`是做为联合的结果而创建。`Symbols`连接了树里的声明节点和其它对同一个实体的声明。`Symbols`是语义系统的基本构建块。 +- **Type:** `Type`是语义系统的其它部分。`Type`可能被命名(比如,类和接口),或匿名(比如,对象类型)。 +- **Signature:** 一共有三种`Signature`类型:调用签名(call),构造签名(construct)和索引签名(index)。 ## 编译过程概述 整个过程从预处理开始。 预处理器会算出哪些文件参与编译,它会去查找如下引用(`/// `标签和`import`语句)。 -语法分析器(Parser)生成抽象语法树(AST)`Node`. 这些仅为用户输出的抽象表现,以树的形式。 一个`SourceFile`对象表示一个给定文件的AST并且带有一些额外的信息如文件名及源文件内容。 +语法分析器(Parser)生成抽象语法树(AST)`Node`. 这些仅为用户输出的抽象表现,以树的形式。 一个`SourceFile`对象表示一个给定文件的 AST 并且带有一些额外的信息如文件名及源文件内容。 -然后,联合器(Binder)处理AST节点,结合并生成`Symbols`。 一个`Symbol`会对应到一个命名实体。 这里有个一微妙的差别,几个声明节点可能会是名字相同的实体。 也就是说,有时候不同的`Node`具有相同的`Symbol`,并且每个`Symbol`保持跟踪它的声明节点。 比如,一个名字相同的`class`和`namespace`可以_合并_,并且拥有相同的`Symbol`。 联合器也会处理作用域,以确保每个`Symbol`都在正确的封闭作用域里创建。 +然后,联合器(Binder)处理 AST 节点,结合并生成`Symbols`。 一个`Symbol`会对应到一个命名实体。 这里有个一微妙的差别,几个声明节点可能会是名字相同的实体。 也就是说,有时候不同的`Node`具有相同的`Symbol`,并且每个`Symbol`保持跟踪它的声明节点。 比如,一个名字相同的`class`和`namespace`可以*合并*,并且拥有相同的`Symbol`。 联合器也会处理作用域,以确保每个`Symbol`都在正确的封闭作用域里创建。 生成`SourceFile`(还带有它的`Symbols`们)是通过调用`createSourceFile` API。 到目前为止,`Symbol`代表的命名实体可以在单个文件里看到,但是有些声明可以从多文件合并,因此下一步就是构建一个全局的包含所有文件的视图,也就是创建一个`Program`。 -一个`Program`是`SourceFile`的集合并带有一系列`CompilerOptions`。 通过调用`createProgram` API来创建`Program`。 +一个`Program`是`SourceFile`的集合并带有一系列`CompilerOptions`。 通过调用`createProgram` API 来创建`Program`。 -通过一个`Program`实例创建`TypeChecker`。 `TypeChecker`是TypeScript类型系统的核心。 它负责计算出不同文件里的`Symbols`之间的关系,将`Type`赋值给`Symbol`,并生成任何语义`Diagnostic`(比如:error)。 +通过一个`Program`实例创建`TypeChecker`。 `TypeChecker`是 TypeScript 类型系统的核心。 它负责计算出不同文件里的`Symbols`之间的关系,将`Type`赋值给`Symbol`,并生成任何语义`Diagnostic`(比如:error)。 `TypeChecker`首先要做的是合并不同的`SourceFile`里的`Symbol`到一个单独的视图,创建单一的`Symbol`表,合并所有普通的`Symbol`(比如:不同文件里的`namespace`)。 在原始状态初始化完成后,`TypeChecker`就可以解决关于这个程序的任何问题了。 这些“问题”可以是: -* 这个`Node`的`Symbol`是什么? -* 这个`Symbol`的`Type`是什么? -* 在AST的某个部分里有哪些`Symbol`是可见的? -* 某个函数声明的`Signature`都有哪些? -* 针对某个文件应该报哪些错误? +- 这个`Node`的`Symbol`是什么? +- 这个`Symbol`的`Type`是什么? +- 在 AST 的某个部分里有哪些`Symbol`是可见的? +- 某个函数声明的`Signature`都有哪些? +- 针对某个文件应该报哪些错误? `TypeChecker`计算所有东西都是“懒惰的”;为了回答一个问题它仅“解决”必要的信息。 `TypeChecker`仅会检测和这个问题有关的`Node`,`Symbol`或`Type`,不会检测额外的实体。 @@ -65,16 +65,16 @@ 令牌本身就具有我们称为一个“完整开始”和一个“令牌开始”。“令牌开始”是指更自然的版本,它表示在文件中令牌开始的位置。“完整开始”是指从上一个有意义的令牌之后扫描器开始扫描的起始位置。当关心琐事时,我们往往更关心完整开始。 -| 函数 | 描述 | -| :--- | :--- | -| `ts.Node.getStart` | 取得某节点的第一个令牌起始位置。 | +| 函数 | 描述 | +| :--------------------- | :------------------------------------- | +| `ts.Node.getStart` | 取得某节点的第一个令牌起始位置。 | | `ts.Node.getFullStart` | 取得某节点拥有的第一个令牌的完整开始。 | ### **琐碎内容(Trivia)** 语法的琐碎内容代表源码里那些对理解代码无关紧要的内容,比如空白,注释甚至一些冲突的标记。 -因为琐碎内容不是语言正常语法的一部分(不包括ECMAScript API规范)并且可能在任意2个令牌中的任意位置出现,它们不会包含在语法树里。但是,因为它们对于像重构和维护高保真源码很重要,所以需要的时候还是能够通过我们的APIs访问。 +因为琐碎内容不是语言正常语法的一部分(不包括 ECMAScript API 规范)并且可能在任意 2 个令牌中的任意位置出现,它们不会包含在语法树里。但是,因为它们对于像重构和维护高保真源码很重要,所以需要的时候还是能够通过我们的 APIs 访问。 因为`EndOfFileToken`后面可以没有任何内容(令牌和琐碎内容),所有琐碎内容自然地在非琐碎内容之前,而且存在于那个令牌的“完整开始”和“令牌开始”之间。 @@ -87,8 +87,7 @@ var x = 10; // This is x. * Postcondition: Grants all three wishes. */ function genie([wish1, wish2, wish3]: [Wish, Wish, Wish]) { - while (true) { - } + while (true) {} } // End function ``` @@ -98,10 +97,10 @@ function genie([wish1, wish2, wish3]: [Wish, Wish, Wish]) { 对于大多数普通用户,注释是“有趣的”琐碎内容。属于一个节点的注释内容可以通过下面的函数来获取: -| 函数 | 描述 | -| :--- | :--- | -| `ts.getLeadingCommentRanges` | 提供源文件和一个指定位置,返回指定位置后的第一个换行与令牌之间的注释的范围(与`ts.Node.getFullStart`配合会更有用)。 | -| `ts.getTrailingCommentRanges` | 提供源文件和一个指定位置,返回到指定位置后第一个换行为止的注释的范围(与`ts.Node.getEnd`配合会更有用)。 | +| 函数 | 描述 | +| :---------------------------- | :------------------------------------------------------------------------------------------------------------------- | +| `ts.getLeadingCommentRanges` | 提供源文件和一个指定位置,返回指定位置后的第一个换行与令牌之间的注释的范围(与`ts.Node.getFullStart`配合会更有用)。 | +| `ts.getTrailingCommentRanges` | 提供源文件和一个指定位置,返回到指定位置后第一个换行为止的注释的范围(与`ts.Node.getEnd`配合会更有用)。 | 做为例子,假设有下面一部分源代码: @@ -111,7 +110,7 @@ debugger;/*hello*/ /*hi*/ function ``` -`function`关键字的完整开始是从`/*hello*/`注释,但是`getLeadingCommentRanges`仅会返回后面2个注释: +`function`关键字的完整开始是从`/*hello*/`注释,但是`getLeadingCommentRanges`仅会返回后面 2 个注释: ```text d e b u g g e r ; / * h e l l o * / _ _ _ _ _ [CR] [NL] _ _ _ _ / / b y e [CR] [NL] _ _ / * h i * / _ _ _ _ f u n c t i o n @@ -123,4 +122,3 @@ d e b u g g e r ; / * h e l l o * / _ _ _ _ _ [CR] [NL] _ _ _ _ / / b y e [CR] [ 适当地,在`debugger`语句后调用`getTrailingCommentRanges`可以提取出`/*hello*/`注释。 如果你关心令牌流的更多信息,`createScanner`也有一个`skipTrivia`标记,你可以设置成`false`,然后使用`setText`/`setTextPos`来扫描文件里的不同位置。 - diff --git a/zh/wiki/coding_guidelines.md b/zh/wiki/coding_guidelines.md index d162d4d1..ac00b062 100644 --- a/zh/wiki/coding_guidelines.md +++ b/zh/wiki/coding_guidelines.md @@ -1,20 +1,20 @@ # 编码规范 -这个编码规范是给TypeScript开发团队在开发TypeScript时使用的。 对于使用TypeScript的普通用户来说不一定适用,但是可以做为一个参考。 +这个编码规范是给 TypeScript 开发团队在开发 TypeScript 时使用的。 对于使用 TypeScript 的普通用户来说不一定适用,但是可以做为一个参考。 ## 命名 -1. 使用PascalCase为类型命名。 +1. 使用 PascalCase 为类型命名。 2. 不要使用`I`做为接口名前缀。 -3. 使用PascalCase为枚举值命名。 -4. 使用camelCase为函数命名。 -5. 使用camelCase为属性或本地变量命名。 +3. 使用 PascalCase 为枚举值命名。 +4. 使用 camelCase 为函数命名。 +5. 使用 camelCase 为属性或本地变量命名。 6. 不要为私有属性名添加`_`前缀。 7. 尽可能使用完整的单词拼写命名。 ## 组件 -1. 1个文件对应一个逻辑组件 (比如:解析器,检查器)。 +1. 1 个文件对应一个逻辑组件 (比如:解析器,检查器)。 2. 不要添加新的文件。 :\) 3. `.generated.*`后缀的文件是自动生成的,不要手动改它。 @@ -31,7 +31,7 @@ ## 一般假设 -1. 假设像Nodes,Symbols等这样的对象在定义它的组件外部是不可改变的。不要去改变它们。 +1. 假设像 Nodes,Symbols 等这样的对象在定义它的组件外部是不可改变的。不要去改变它们。 2. 假设数组是不能改变的。 ## 类 @@ -40,57 +40,56 @@ ## 标记 -1. 一个类型中有超过2个布尔属性时,把它变成一个标记。 +1. 一个类型中有超过 2 个布尔属性时,把它变成一个标记。 ## 注释 -为函数,接口,枚举类型和类使用JSDoc风格的注释。 +为函数,接口,枚举类型和类使用 JSDoc 风格的注释。 ## 字符串 1. 使用双引号`""` -2. 所有要展示给用户看的信息字符串都要做好本地化工作(在diagnosticMessages.json中创建新的实体)。 +2. 所有要展示给用户看的信息字符串都要做好本地化工作(在 diagnosticMessages.json 中创建新的实体)。 ## 错误提示信息 1. 在句子结尾使用`.`。 2. 对不确定的实体使用不定冠词。 3. 确切的实体应该使用名字(变量名,类型名等) -4. 当创建一条新的规则时,主题应该使用单数形式(比如:An external module cannot...而不是External modules cannot)。 +4. 当创建一条新的规则时,主题应该使用单数形式(比如:An external module cannot...而不是 External modules cannot)。 5. 使用现在时态。 ## 错误提示信息代码 -提示信息被划分类成了一般的区间。如果要新加一个提示信息,在上条代码上加1做为新的代码。 +提示信息被划分类成了一般的区间。如果要新加一个提示信息,在上条代码上加 1 做为新的代码。 -* 1000 语法信息 -* 2000 语言信息 -* 4000 声明生成信息 -* 5000 编译器选项信息 -* 6000 命令行编译器信息 -* 7000 noImplicitAny信息 +- 1000 语法信息 +- 2000 语言信息 +- 4000 声明生成信息 +- 5000 编译器选项信息 +- 6000 命令行编译器信息 +- 7000 noImplicitAny 信息 ## 普通方法 由于种种原因,我们避免使用一些方法,而使用我们自己定义的。 -1. 不使用ECMAScript 5函数;而是使用[core.ts](https://github.com/Microsoft/TypeScript/blob/master/src/compiler/core.ts)这里的。 +1. 不使用 ECMAScript 5 函数;而是使用[core.ts](https://github.com/Microsoft/TypeScript/blob/master/src/compiler/core.ts)这里的。 2. 不要使用`for..in`语句;而是使用`ts.forEach`,`ts.forEachKey`和`ts.forEachValue`。注意它们之间的区别。 3. 如果可能的话,尝试使用`ts.forEach`,`ts.map`和`ts.filter`代替循环。 ## 风格 -1. 使用arrow函数代替匿名函数表达式。 -2. 只要需要的时候才把arrow函数的参数括起来。 比如,`(x) => x + x`是错误的,下面是正确的做法: +1. 使用 arrow 函数代替匿名函数表达式。 +2. 只要需要的时候才把 arrow 函数的参数括起来。 比如,`(x) => x + x`是错误的,下面是正确的做法: 1. `x => x + x` 2. `(x,y) => x + y` 3. `(x: T, y: T) => x === y` 3. 总是使用`{}`把循环体和条件语句括起来。 4. 开始的`{`总是在同一行。 -5. 小括号里开始不要有空白. 逗号,冒号,分号后要有一个空格。比如: +5. 小括号里开始不要有空白. 逗号,冒号,分号后要有一个空格。比如: 1. `for (var i = 0, n = str.length; i < 10; i++) { }` 2. `if (x < 10) { }` 3. `function f(x: number, y: string): void { }` -6. 每个变量声明语句只声明一个变量 (比如 使用 `var x = 1; var y = 2;` 而不是 `var x = 1, y = 2;`)。 +6. 每个变量声明语句只声明一个变量 (比如 使用 `var x = 1; var y = 2;` 而不是 `var x = 1, y = 2;`)。 7. `else`要在结束的`}`后另起一行。 - diff --git a/zh/wiki/common-errors.md b/zh/wiki/common-errors.md index a5023187..07fe9f75 100644 --- a/zh/wiki/common-errors.md +++ b/zh/wiki/common-errors.md @@ -2,7 +2,7 @@ ## 介绍 -下面列出了一些在使用TypeScript语言和编译器过程中常见的容易让人感到困惑的错误信息。 +下面列出了一些在使用 TypeScript 语言和编译器过程中常见的容易让人感到困惑的错误信息。 ## 令人困惑的常见错误 @@ -10,11 +10,10 @@ _修复:_ -* 检查文件编码,确保为UTF-8 - [https://typescript.codeplex.com/workitem/1587](https://typescript.codeplex.com/workitem/1587) +- 检查文件编码,确保为 UTF-8 - [https://typescript.codeplex.com/workitem/1587](https://typescript.codeplex.com/workitem/1587) ### external module XYZ cannot be resolved _修复:_ -* 检查模块路径是否大小写敏感 - [https://typescript.codeplex.com/workitem/2134](https://typescript.codeplex.com/workitem/2134) - +- 检查模块路径是否大小写敏感 - [https://typescript.codeplex.com/workitem/2134](https://typescript.codeplex.com/workitem/2134) diff --git a/zh/wiki/roadmap.md b/zh/wiki/roadmap.md index 3c9577a8..70f93cc1 100644 --- a/zh/wiki/roadmap.md +++ b/zh/wiki/roadmap.md @@ -2,130 +2,129 @@ ## 2.1 -* 调查 [Function bind 操作符](https://github.com/Microsoft/TypeScript/issues/3508) -* [支持工程引用](https://github.com/Microsoft/TypeScript/issues/3469) -* [`readonly` 修饰符](https://github.com/Microsoft/TypeScript/issues/12) -* 调查 [具名类型支持](https://github.com/Microsoft/TypeScript/issues/202) -* Language Service API里支持代码重构功能 -* [扁平化声明](https://github.com/Microsoft/TypeScript/issues/4433) +- 调查 [Function bind 操作符](https://github.com/Microsoft/TypeScript/issues/3508) +- [支持工程引用](https://github.com/Microsoft/TypeScript/issues/3469) +- [`readonly` 修饰符](https://github.com/Microsoft/TypeScript/issues/12) +- 调查 [具名类型支持](https://github.com/Microsoft/TypeScript/issues/202) +- Language Service API 里支持代码重构功能 +- [扁平化声明](https://github.com/Microsoft/TypeScript/issues/4433) ## 2.0 -* 切换到[基于转换的生成器](https://github.com/Microsoft/TypeScript/issues/5595) -* [支持ES5/ES3 `async`/`await`](https://github.com/Microsoft/TypeScript/issues/1664) -* 支持[ES7对象属性展开及剩余属性](https://github.com/Microsoft/TypeScript/issues/2103) -* [规定函数的`this`类型](https://github.com/Microsoft/TypeScript/issues/3694) -* [属性访问上的类型保护](https://github.com/Microsoft/TypeScript/issues/186) -* [切换类型保护](https://github.com/Microsoft/TypeScript/issues/2214) -* 支持[常量和Symbol上计算属性的类型检查](https://github.com/Microsoft/TypeScript/issues/5579) -* [可变类型](https://github.com/Microsoft/TypeScript/issues/5453) -* [外部装饰器](https://github.com/Microsoft/TypeScript/issues/2900) -* [弃用的装饰器](https://github.com/Microsoft/TypeScript/issues/390) -* [条件装饰器](https://github.com/Microsoft/TypeScript/issues/3538) -* 函数表达式及箭头函数的装饰器 -* [支持节点注册勾子](https://github.com/Microsoft/TypeScript/issues/1823) -* [在tsconfig.json里支持Glob](https://github.com/Microsoft/TypeScript/issues/1927) -* 在语言服务API里支持快速修复 -* 在tsserver/语言服务API里集成tsd -* [从js文件的JSDoc里撮类型信息](https://github.com/Microsoft/TypeScript/issues/4790) -* [增强lib.d.ts模块化](https://github.com/Microsoft/TypeScript/issues/494) -* 支持[外部辅助代码库](https://github.com/Microsoft/TypeScript/issues/3364) -* 调查[语言服务的可扩展性](https://github.com/Microsoft/TypeScript/issues/6508) +- 切换到[基于转换的生成器](https://github.com/Microsoft/TypeScript/issues/5595) +- [支持 ES5/ES3 `async`/`await`](https://github.com/Microsoft/TypeScript/issues/1664) +- 支持[ES7 对象属性展开及剩余属性](https://github.com/Microsoft/TypeScript/issues/2103) +- [规定函数的`this`类型](https://github.com/Microsoft/TypeScript/issues/3694) +- [属性访问上的类型保护](https://github.com/Microsoft/TypeScript/issues/186) +- [切换类型保护](https://github.com/Microsoft/TypeScript/issues/2214) +- 支持[常量和 Symbol 上计算属性的类型检查](https://github.com/Microsoft/TypeScript/issues/5579) +- [可变类型](https://github.com/Microsoft/TypeScript/issues/5453) +- [外部装饰器](https://github.com/Microsoft/TypeScript/issues/2900) +- [弃用的装饰器](https://github.com/Microsoft/TypeScript/issues/390) +- [条件装饰器](https://github.com/Microsoft/TypeScript/issues/3538) +- 函数表达式及箭头函数的装饰器 +- [支持节点注册勾子](https://github.com/Microsoft/TypeScript/issues/1823) +- [在 tsconfig.json 里支持 Glob](https://github.com/Microsoft/TypeScript/issues/1927) +- 在语言服务 API 里支持快速修复 +- 在 tsserver/语言服务 API 里集成 tsd +- [从 js 文件的 JSDoc 里撮类型信息](https://github.com/Microsoft/TypeScript/issues/4790) +- [增强 lib.d.ts 模块化](https://github.com/Microsoft/TypeScript/issues/494) +- 支持[外部辅助代码库](https://github.com/Microsoft/TypeScript/issues/3364) +- 调查[语言服务的可扩展性](https://github.com/Microsoft/TypeScript/issues/6508) ## 1.8 -* [在TypeScript编译时使用`--allowjs`允许JavaScript](https://github.com/Microsoft/TypeScript/issues/4792) -* [在循环里允许捕获的`let`/`const`](https://github.com/Microsoft/TypeScript/issues/3915) -* [标记死代码](https://github.com/Microsoft/TypeScript/pull/4788) -* [使用`--outFile`连接模块输出](https://github.com/Microsoft/TypeScript/pull/5090) -* [tsconfig.json里支持注释](https://github.com/Microsoft/TypeScript/issues/4987) -* [使用`--pretty`为终端里的错误信息添加样式](https://github.com/Microsoft/TypeScript/pull/5140) -* [支持`--outFile`给命名的管道套接字和特殊设备](https://github.com/Microsoft/TypeScript/issues/4841) -* [支持使用名字字面量的计算属性](https://github.com/Microsoft/TypeScript/issues/4653) -* [字符串字面量类型](https://github.com/Microsoft/TypeScript/pull/5185) -* [JSX无状态的功能性组件](https://github.com/Microsoft/TypeScript/issues/5478) -* [优化联合/交类型接口](https://github.com/Microsoft/TypeScript/pull/5738) -* [支持F-Bounded多态性](https://github.com/Microsoft/TypeScript/pull/5949) -* [支持全路径`-project`/`-p`参数](https://github.com/Microsoft/TypeScript/issues/2869) -* [在SystemJS使用`--allowSyntheticDefaultImports`支持`default`导入操作](https://github.com/Microsoft/TypeScript/issues/5285) -* [识别JavaScript里原型的赋值](https://github.com/Microsoft/TypeScript/pull/5876) -* [在模块里使用路径映射](https://github.com/Microsoft/TypeScript/issues/5039) -* [在其它模块里增加global/module作用域](https://github.com/Microsoft/TypeScript/issues/4166) -* [在Visual Studio使用tsconfig.json做为高优先级的配置](https://github.com/Microsoft/TypeScript/issues/5287) -* [基于`this`类型保护](https://github.com/Microsoft/TypeScript/pull/5906) -* 支持[自定义JSX工厂通过`--reactNamespace`](https://github.com/Microsoft/TypeScript/pull/6146) -* [增强for-in语句检查](https://github.com/Microsoft/TypeScript/pull/6379) -* [JSX代码在VS 2015里高亮](https://github.com/Microsoft/TypeScript/issues/4835) -* 发布[TypeScript NuGet 包](https://github.com/Microsoft/TypeScript/issues/3940) +- [在 TypeScript 编译时使用`--allowjs`允许 JavaScript](https://github.com/Microsoft/TypeScript/issues/4792) +- [在循环里允许捕获的`let`/`const`](https://github.com/Microsoft/TypeScript/issues/3915) +- [标记死代码](https://github.com/Microsoft/TypeScript/pull/4788) +- [使用`--outFile`连接模块输出](https://github.com/Microsoft/TypeScript/pull/5090) +- [tsconfig.json 里支持注释](https://github.com/Microsoft/TypeScript/issues/4987) +- [使用`--pretty`为终端里的错误信息添加样式](https://github.com/Microsoft/TypeScript/pull/5140) +- [支持`--outFile`给命名的管道套接字和特殊设备](https://github.com/Microsoft/TypeScript/issues/4841) +- [支持使用名字字面量的计算属性](https://github.com/Microsoft/TypeScript/issues/4653) +- [字符串字面量类型](https://github.com/Microsoft/TypeScript/pull/5185) +- [JSX 无状态的功能性组件](https://github.com/Microsoft/TypeScript/issues/5478) +- [优化联合/交类型接口](https://github.com/Microsoft/TypeScript/pull/5738) +- [支持 F-Bounded 多态性](https://github.com/Microsoft/TypeScript/pull/5949) +- [支持全路径`-project`/`-p`参数](https://github.com/Microsoft/TypeScript/issues/2869) +- [在 SystemJS 使用`--allowSyntheticDefaultImports`支持`default`导入操作](https://github.com/Microsoft/TypeScript/issues/5285) +- [识别 JavaScript 里原型的赋值](https://github.com/Microsoft/TypeScript/pull/5876) +- [在模块里使用路径映射](https://github.com/Microsoft/TypeScript/issues/5039) +- [在其它模块里增加 global/module 作用域](https://github.com/Microsoft/TypeScript/issues/4166) +- [在 Visual Studio 使用 tsconfig.json 做为高优先级的配置](https://github.com/Microsoft/TypeScript/issues/5287) +- [基于`this`类型保护](https://github.com/Microsoft/TypeScript/pull/5906) +- 支持[自定义 JSX 工厂通过`--reactNamespace`](https://github.com/Microsoft/TypeScript/pull/6146) +- [增强 for-in 语句检查](https://github.com/Microsoft/TypeScript/pull/6379) +- [JSX 代码在 VS 2015 里高亮](https://github.com/Microsoft/TypeScript/issues/4835) +- 发布[TypeScript NuGet 包](https://github.com/Microsoft/TypeScript/issues/3940) ## 1.7 -* [ES7幂运算符](https://github.com/Microsoft/TypeScript/issues/4812) -* [多态的`this`类型](https://github.com/Microsoft/TypeScript/pull/4910) -* [支持`--module`的`--target es6`](https://github.com/Microsoft/TypeScript/issues/4806) -* [支持目标为ES3时使用装饰器](https://github.com/Microsoft/TypeScript/pull/4741) -* [为ES6支持`async`/`await`\(Node v4\)](https://github.com/Microsoft/TypeScript/pull/5231) -* [增强的字面量初始化器解构检查](https://github.com/Microsoft/TypeScript/pull/4598) +- [ES7 幂运算符](https://github.com/Microsoft/TypeScript/issues/4812) +- [多态的`this`类型](https://github.com/Microsoft/TypeScript/pull/4910) +- [支持`--module`的`--target es6`](https://github.com/Microsoft/TypeScript/issues/4806) +- [支持目标为 ES3 时使用装饰器](https://github.com/Microsoft/TypeScript/pull/4741) +- [为 ES6 支持`async`/`await`\(Node v4\)](https://github.com/Microsoft/TypeScript/pull/5231) +- [增强的字面量初始化器解构检查](https://github.com/Microsoft/TypeScript/pull/4598) ## 1.6 -* [ES6 Generators](https://github.com/Microsoft/TypeScript/issues/2873) -* [Local types](https://github.com/Microsoft/TypeScript/pull/3266) -* [泛型别名](https://github.com/Microsoft/TypeScript/issues/1616) -* [类继承语句里使用表达式](https://github.com/Microsoft/TypeScript/pull/3516) -* [Class表达式](https://github.com/Microsoft/TypeScript/issues/497) -* [tsconfig.json的`exclude`属性](https://github.com/Microsoft/TypeScript/pull/3188) -* [用户定义的类型保护函数](https://github.com/Microsoft/TypeScript/issues/1007) -* [增强外部模块解析](https://github.com/Microsoft/TypeScript/issues/2338) -* [JSX支持](https://github.com/Microsoft/TypeScript/pull/3564) -* [交叉类型](https://github.com/Microsoft/TypeScript/pull/3622) -* [`abstract`类和方法](https://github.com/Microsoft/TypeScript/issues/3578) -* [严格的对象字面量赋值检查](https://github.com/Microsoft/TypeScript/pull/3823) -* [类和接口的声明合并](https://github.com/Microsoft/TypeScript/pull/3333) -* 新增[--init](https://github.com/Microsoft/TypeScript/issues/3079) +- [ES6 Generators](https://github.com/Microsoft/TypeScript/issues/2873) +- [Local types](https://github.com/Microsoft/TypeScript/pull/3266) +- [泛型别名](https://github.com/Microsoft/TypeScript/issues/1616) +- [类继承语句里使用表达式](https://github.com/Microsoft/TypeScript/pull/3516) +- [Class 表达式](https://github.com/Microsoft/TypeScript/issues/497) +- [tsconfig.json 的`exclude`属性](https://github.com/Microsoft/TypeScript/pull/3188) +- [用户定义的类型保护函数](https://github.com/Microsoft/TypeScript/issues/1007) +- [增强外部模块解析](https://github.com/Microsoft/TypeScript/issues/2338) +- [JSX 支持](https://github.com/Microsoft/TypeScript/pull/3564) +- [交叉类型](https://github.com/Microsoft/TypeScript/pull/3622) +- [`abstract`类和方法](https://github.com/Microsoft/TypeScript/issues/3578) +- [严格的对象字面量赋值检查](https://github.com/Microsoft/TypeScript/pull/3823) +- [类和接口的声明合并](https://github.com/Microsoft/TypeScript/pull/3333) +- 新增[--init](https://github.com/Microsoft/TypeScript/issues/3079) ## 1.5 -* 支持[解构](https://github.com/Microsoft/TypeScript/pull/1346) -* 支持[展开操作符](https://github.com/Microsoft/TypeScript/pull/1931) -* 支持[ES6模块](https://github.com/Microsoft/TypeScript/issues/2242) -* 支持[for..of](https://github.com/Microsoft/TypeScript/pull/2207) -* 支持[ES6 Unicode 规范](https://github.com/Microsoft/TypeScript/pull/2169) -* 支持[Symbols](https://github.com/Microsoft/TypeScript/pull/1978) -* 支持[计算属性](https://github.com/Microsoft/TypeScript/issues/1082) -* 支持[tsconfig.json文件](https://github.com/Microsoft/TypeScript/pull/1692) -* 支持[ES3/ES5的let和const](https://github.com/Microsoft/TypeScript/pull/2161) -* 支持[ES3/ES5带标记的模版](https://github.com/Microsoft/TypeScript/pull/1589) -* 暴露一个新的编辑器接口通过[TS Server](https://github.com/Microsoft/TypeScript/pull/2041) -* 支持[ES7 装饰器提案](https://github.com/Microsoft/TypeScript/issues/2249) -* 支持[装饰器类型元信息](https://github.com/Microsoft/TypeScript/pull/2589) -* 新增[--rootDir](https://github.com/Microsoft/TypeScript/pull/2772) -* 新增[ts.transpile API](https://github.com/Microsoft/TypeScript/issues/2499) -* 支持[--module umd](https://github.com/Microsoft/TypeScript/issues/2036) -* 支持[--module system](https://github.com/Microsoft/TypeScript/issues/2616) -* 新增[--noEmitHelpers](https://github.com/Microsoft/TypeScript/pull/2901) -* 新增[--inlineSourceMap](https://github.com/Microsoft/TypeScript/pull/2484) -* 新增[--inlineSources](https://github.com/Microsoft/TypeScript/pull/2484) -* 新增[--newLine](https://github.com/Microsoft/TypeScript/pull/2921) -* 新增[--isolatedModules](https://github.com/Microsoft/TypeScript/issues/2499) -* 支持新的[`namespace`关键字](https://github.com/Microsoft/TypeScript/issues/2159) -* 支持[Visual Studio 2015的tsconfig.json](https://github.com/Microsoft/TypeScript/issues/3124) -* 增强[Visual Studio 2013的模块字面量高亮](https://github.com/Microsoft/TypeScript/pull/2026) +- 支持[解构](https://github.com/Microsoft/TypeScript/pull/1346) +- 支持[展开操作符](https://github.com/Microsoft/TypeScript/pull/1931) +- 支持[ES6 模块](https://github.com/Microsoft/TypeScript/issues/2242) +- 支持[for..of](https://github.com/Microsoft/TypeScript/pull/2207) +- 支持[ES6 Unicode 规范](https://github.com/Microsoft/TypeScript/pull/2169) +- 支持[Symbols](https://github.com/Microsoft/TypeScript/pull/1978) +- 支持[计算属性](https://github.com/Microsoft/TypeScript/issues/1082) +- 支持[tsconfig.json 文件](https://github.com/Microsoft/TypeScript/pull/1692) +- 支持[ES3/ES5 的 let 和 const](https://github.com/Microsoft/TypeScript/pull/2161) +- 支持[ES3/ES5 带标记的模版](https://github.com/Microsoft/TypeScript/pull/1589) +- 暴露一个新的编辑器接口通过[TS Server](https://github.com/Microsoft/TypeScript/pull/2041) +- 支持[ES7 装饰器提案](https://github.com/Microsoft/TypeScript/issues/2249) +- 支持[装饰器类型元信息](https://github.com/Microsoft/TypeScript/pull/2589) +- 新增[--rootDir](https://github.com/Microsoft/TypeScript/pull/2772) +- 新增[ts.transpile API](https://github.com/Microsoft/TypeScript/issues/2499) +- 支持[--module umd](https://github.com/Microsoft/TypeScript/issues/2036) +- 支持[--module system](https://github.com/Microsoft/TypeScript/issues/2616) +- 新增[--noEmitHelpers](https://github.com/Microsoft/TypeScript/pull/2901) +- 新增[--inlineSourceMap](https://github.com/Microsoft/TypeScript/pull/2484) +- 新增[--inlineSources](https://github.com/Microsoft/TypeScript/pull/2484) +- 新增[--newLine](https://github.com/Microsoft/TypeScript/pull/2921) +- 新增[--isolatedModules](https://github.com/Microsoft/TypeScript/issues/2499) +- 支持新的[`namespace`关键字](https://github.com/Microsoft/TypeScript/issues/2159) +- 支持[Visual Studio 2015 的 tsconfig.json](https://github.com/Microsoft/TypeScript/issues/3124) +- 增强[Visual Studio 2013 的模块字面量高亮](https://github.com/Microsoft/TypeScript/pull/2026) ## 1.4 -* 支持[联合类型和类型保护](https://github.com/Microsoft/TypeScript/pull/824) -* 新增[--noEmitOnError](https://github.com/Microsoft/TypeScript/pull/966) -* 新增[--target ES6](https://github.com/Microsoft/TypeScript/commit/873c1df74b7c7dcba59eaccc1bb4bd4b0da18a35) -* 支持[Let and Const](https://github.com/Microsoft/TypeScript/pull/904) -* 支持[模块字面量](https://github.com/Microsoft/TypeScript/pull/960) -* Library typings for ES6 -* 支持[Const enums](https://github.com/Microsoft/TypeScript/issues/1029) -* 导出语言服务公共API +- 支持[联合类型和类型保护](https://github.com/Microsoft/TypeScript/pull/824) +- 新增[--noEmitOnError](https://github.com/Microsoft/TypeScript/pull/966) +- 新增[--target ES6](https://github.com/Microsoft/TypeScript/commit/873c1df74b7c7dcba59eaccc1bb4bd4b0da18a35) +- 支持[Let and Const](https://github.com/Microsoft/TypeScript/pull/904) +- 支持[模块字面量](https://github.com/Microsoft/TypeScript/pull/960) +- Library typings for ES6 +- 支持[Const enums](https://github.com/Microsoft/TypeScript/issues/1029) +- 导出语言服务公共 API ## 1.3 -* 为新的编译器重写语言服务 -* 支持[受保护的成员](https://github.com/Microsoft/TypeScript/pull/688) in classes -* 支持[元组类型](https://github.com/Microsoft/TypeScript/pull/428) - +- 为新的编译器重写语言服务 +- 支持[受保护的成员](https://github.com/Microsoft/TypeScript/pull/688) in classes +- 支持[元组类型](https://github.com/Microsoft/TypeScript/pull/428) diff --git a/zh/wiki/this-in-typescript.md b/zh/wiki/this-in-typescript.md index 85c97364..2863b32a 100644 --- a/zh/wiki/this-in-typescript.md +++ b/zh/wiki/this-in-typescript.md @@ -1,39 +1,39 @@ -# TypeScript里的this +# TypeScript 里的 this ## 介绍 -在JavaScript里(还有TypeScript),`this`关键字的行为与其它语言相比大为不同。这可能会很令人吃惊,特别是对于那些使用其它语言的用户,他们凭借其直觉来想象`this`关键字的行为。 +在 JavaScript 里(还有 TypeScript),`this`关键字的行为与其它语言相比大为不同。这可能会很令人吃惊,特别是对于那些使用其它语言的用户,他们凭借其直觉来想象`this`关键字的行为。 -这篇文章会教你怎么识别及调试TypeScript里的`this`问题,并且提供了一些解决方案和各自的利弊。 +这篇文章会教你怎么识别及调试 TypeScript 里的`this`问题,并且提供了一些解决方案和各自的利弊。 ## 典型症状和危险系数 丢失`this`上下文的典型症状包括: -* 类的某字段(`this.foo`)为`undefined`,但其它值没有问题 -* `this`的值指向全局的`window`对象而不是类实例对象(在非严格模式下) -* `this`的值为`undefined`而不是类实例对象(严格模式下) -* 调用类方法(`this.doBa()`)失败,错误信息如“TypeError: undefined is not a function”,“Object doesn't support property or method 'doBar'”或“this.doBar is not a function” +- 类的某字段(`this.foo`)为`undefined`,但其它值没有问题 +- `this`的值指向全局的`window`对象而不是类实例对象(在非严格模式下) +- `this`的值为`undefined`而不是类实例对象(严格模式下) +- 调用类方法(`this.doBa()`)失败,错误信息如“TypeError: undefined is not a function”,“Object doesn't support property or method 'doBar'”或“this.doBar is not a function” 程序中应该出现了以下代码: -* 事件监听,比如`window.addEventListener('click', myClass.doThing);` -* Promise解决,比如`myPromise.then(myClass.theNextThing);` -* 第三方库回调,比如`$(document).ready(myClass.start);` -* 函数回调,比如`someArray.map(myClass.convert)` -* ViewModel类型的库里的类,比如`
` -* 可选包里的函数,比如`$.ajax(url, { success: myClass.handleData })` +- 事件监听,比如`window.addEventListener('click', myClass.doThing);` +- Promise 解决,比如`myPromise.then(myClass.theNextThing);` +- 第三方库回调,比如`$(document).ready(myClass.start);` +- 函数回调,比如`someArray.map(myClass.convert)` +- ViewModel 类型的库里的类,比如`
` +- 可选包里的函数,比如`$.ajax(url, { success: myClass.handleData })` -## JavaScript里的`this`究竟是什么? +## JavaScript 里的`this`究竟是什么? -已经有大量的文章讲述了JavaScript里`this`关键字的危险性。查看[这里](http://www.quirksmode.org/js/this.html),[这里](http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it/),或[这里](http://bjorn.tipling.com/all-this)。 +已经有大量的文章讲述了 JavaScript 里`this`关键字的危险性。查看[这里](http://www.quirksmode.org/js/this.html),[这里](http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it/),或[这里](http://bjorn.tipling.com/all-this)。 -当JavaScript里的一个函数被调用时,你可以按照下面的顺序来推断出`this`指向的是什么(这些规则是按优先级顺序排列的): +当 JavaScript 里的一个函数被调用时,你可以按照下面的顺序来推断出`this`指向的是什么(这些规则是按优先级顺序排列的): -* 如果这个函数是`function#bind`调用的结果,那么`this`指向的是传入`bind`的参数 -* 如果函数是以`foo.func()`形式调用的,那么`this`值为`foo` -* 如果是在严格模式下,`this`将为`undefined` -* 否则,`this`将是全局对象(浏览器环境里为`window`) +- 如果这个函数是`function#bind`调用的结果,那么`this`指向的是传入`bind`的参数 +- 如果函数是以`foo.func()`形式调用的,那么`this`值为`foo` +- 如果是在严格模式下,`this`将为`undefined` +- 否则,`this`将是全局对象(浏览器环境里为`window`) 这些规则会产生与直觉相反的效果。比如: @@ -58,7 +58,7 @@ p(); // Prints 'x is undefined' ## `this`的危险信号 -你要注意的最大的危险信号是_在要使用类的方法时没有立即调用它_。任何时候你看到类方法被_引用了_却没有使用相同的表达式来_调用_时,`this`可能已经不对了。 +你要注意的最大的危险信号是*在要使用类的方法时没有立即调用它*。任何时候你看到类方法被*引用了*却没有使用相同的表达式来*调用*时,`this`可能已经不对了。 例子: @@ -79,42 +79,43 @@ window.addEventListener('click', () => x.printThing(), 10); // SAFE, method is i ### 使用实例函数 -代替TypeScript里默认的_原型_方法,你可以使用一个_实例箭头函数_来定义类成员: +代替 TypeScript 里默认的*原型*方法,你可以使用一个*实例箭头函数*来定义类成员: ```typescript class MyClass { - private status = "blah"; + private status = 'blah'; - public run = () => { // <-- note syntax here - alert(this.status); - } + public run = () => { + // <-- note syntax here + alert(this.status); + }; } var x = new MyClass(); $(document).ready(x.run); // SAFE, 'run' will always have correct 'this' ``` -* 好与坏:这会为每个类实例的每个方法创建额外的闭包。如果这个方法通常是正常调用的,那么这么做有点过了。然而,它经常会在回调函数里调用,让类实例捕获到`this`上下文会比在每次调用时都创建一个闭包来得更有效率一些。 -* 好:其它外部使用者不可能忘记处理`this`上下文 -* 好:在TypeScript里是类型安全的 -* 好:如果函数带参数不需要额外的工作 -* 坏:派生类不能通过使用`super`调用基类方法 -* 坏:在类与用户之前产生了额外的非类型安全的约束:明确了哪些方法提前绑定了以及哪些没有 +- 好与坏:这会为每个类实例的每个方法创建额外的闭包。如果这个方法通常是正常调用的,那么这么做有点过了。然而,它经常会在回调函数里调用,让类实例捕获到`this`上下文会比在每次调用时都创建一个闭包来得更有效率一些。 +- 好:其它外部使用者不可能忘记处理`this`上下文 +- 好:在 TypeScript 里是类型安全的 +- 好:如果函数带参数不需要额外的工作 +- 坏:派生类不能通过使用`super`调用基类方法 +- 坏:在类与用户之前产生了额外的非类型安全的约束:明确了哪些方法提前绑定了以及哪些没有 ### 本地的胖箭头 -在TypeScrip里(这里为了讲解添加了一些参数) : +在 TypeScrip 里(这里为了讲解添加了一些参数) : ```typescript var x = new SomeClass(); someCallback((n, m) => x.doSomething(n, m)); ``` -* 好与坏:内存/效能上的利弊与实例函数相比正相反 -* 好:在TypeScript,100%的类型安全 -* 好:在ECMAScript 3里同样生效 -* 好:你只需要输入一次实例名 -* 坏:你要输出2次参数名 -* 坏:对于可变参数不起作用('rest') +- 好与坏:内存/效能上的利弊与实例函数相比正相反 +- 好:在 TypeScript,100%的类型安全 +- 好:在 ECMAScript 3 里同样生效 +- 好:你只需要输入一次实例名 +- 坏:你要输出 2 次参数名 +- 坏:对于可变参数不起作用('rest') ### Function.bind @@ -124,9 +125,8 @@ var x = new SomeClass(); window.setTimeout(x.someMethod.bind(x), 100); ``` -* 好与坏:内存/效能上的利弊与实例函数相比正相反 -* 好:如果函数带参数不需要额外的工作 -* 坏:目前在TypeScript里,不是类型安全的 -* 坏:只在ECMAScript 5里生效 -* 坏:你要输入2次实例名 - +- 好与坏:内存/效能上的利弊与实例函数相比正相反 +- 好:如果函数带参数不需要额外的工作 +- 坏:目前在 TypeScript 里,不是类型安全的 +- 坏:只在 ECMAScript 5 里生效 +- 坏:你要输入 2 次实例名 diff --git a/zh/wiki/typescript-editor-support.md b/zh/wiki/typescript-editor-support.md index bb47f36b..8c501328 100644 --- a/zh/wiki/typescript-editor-support.md +++ b/zh/wiki/typescript-editor-support.md @@ -1,25 +1,25 @@ -# 支持TypeScript的编辑器 +# 支持 TypeScript 的编辑器 ## 快捷列表 -* [Atom](typescript-editor-support.md#atom) -* [Eclipse](typescript-editor-support.md#eclipse) -* [Emacs](typescript-editor-support.md#emacs) -* [NetBeans](typescript-editor-support.md#netbeans) -* [Sublime Text](typescript-editor-support.md#sublime-text) -* [TypeScript Builder](typescript-editor-support.md#typescript-builder) -* [Vim](typescript-editor-support.md#vim) -* [Visual Studio](typescript-editor-support.md#visual-studio-20132015) -* [Visual Studio Code](typescript-editor-support.md#visual-studio-code) -* [WebStorm](typescript-editor-support.md#webstorm) +- [Atom](typescript-editor-support.md#atom) +- [Eclipse](typescript-editor-support.md#eclipse) +- [Emacs](typescript-editor-support.md#emacs) +- [NetBeans](typescript-editor-support.md#netbeans) +- [Sublime Text](typescript-editor-support.md#sublime-text) +- [TypeScript Builder](typescript-editor-support.md#typescript-builder) +- [Vim](typescript-editor-support.md#vim) +- [Visual Studio](typescript-editor-support.md#visual-studio-20132015) +- [Visual Studio Code](typescript-editor-support.md#visual-studio-code) +- [WebStorm](typescript-editor-support.md#webstorm) ## Atom -[Atom-TypeScript](https://atom.io/packages/atom-typescript),由TypeStrong开发的针对Atom的TypeScript语言服务。 +[Atom-TypeScript](https://atom.io/packages/atom-typescript),由 TypeStrong 开发的针对 Atom 的 TypeScript 语言服务。 ## Eclipse -[Eclipse TypeScript 插件](https://github.com/palantir/eclipse-typescript),由Palantir开发的Eclipse插件。 +[Eclipse TypeScript 插件](https://github.com/palantir/eclipse-typescript),由 Palantir 开发的 Eclipse 插件。 ## Emacs @@ -27,32 +27,32 @@ ## NetBeans -* [nbts](https://github.com/Everlaw/nbts) - NetBeans TypeScript editor plugin -* [Geertjan's TypeScript NetBeans Plugin](https://github.com/GeertjanWielenga/TypeScript) +- [nbts](https://github.com/Everlaw/nbts) - NetBeans TypeScript editor plugin +- [Geertjan's TypeScript NetBeans Plugin](https://github.com/GeertjanWielenga/TypeScript) ## Sublime Text -[Sublime的TypeScript插件](https://github.com/Microsoft/TypeScript-Sublime-Plugin),可以通过[Package Control](https://packagecontrol.io/)来安装,支持Sublime Text 2和Sublime Text 3. +[Sublime 的 TypeScript 插件](https://github.com/Microsoft/TypeScript-Sublime-Plugin),可以通过[Package Control](https://packagecontrol.io/)来安装,支持 Sublime Text 2 和 Sublime Text 3. ## TypeScript Builder -[TypeScript Builder](http://www.typescriptbuilder.com/),TypeScript专用IDE. +[TypeScript Builder](http://www.typescriptbuilder.com/),TypeScript 专用 IDE. ## Vim ### 语法高亮 -* [leafgarland/typescript-vim](https://github.com/leafgarland/typescript-vim)提供了语法文件用来高亮显示`.ts`和`.d.ts`。 -* [HerringtonDarkholme/yats.vim](https://github.com/HerringtonDarkholme/yats.vim)提供了更多语法高亮和DOM关键字。 +- [leafgarland/typescript-vim](https://github.com/leafgarland/typescript-vim)提供了语法文件用来高亮显示`.ts`和`.d.ts`。 +- [HerringtonDarkholme/yats.vim](https://github.com/HerringtonDarkholme/yats.vim)提供了更多语法高亮和 DOM 关键字。 ### 语言服务工具 -有两个主要的TypeScript插件: +有两个主要的 TypeScript 插件: -* [Quramy/tsuquyomi](https://github.com/Quramy/tsuquyomi) -* [clausreinke/typescript-tools.vim](https://github.com/clausreinke/typescript-tools.vim) +- [Quramy/tsuquyomi](https://github.com/Quramy/tsuquyomi) +- [clausreinke/typescript-tools.vim](https://github.com/clausreinke/typescript-tools.vim) -如果你想要输出时自动补全功能,你可以安装[YouCompleteMe](https://github.com/Valloric/YouCompleteMe)并添加以下代码到`.vimrc`里,以指定哪些符号能用来触发补全功能。YouCompleteMe会调用它们各自TypeScript插件来进行语义查询。 +如果你想要输出时自动补全功能,你可以安装[YouCompleteMe](https://github.com/Valloric/YouCompleteMe)并添加以下代码到`.vimrc`里,以指定哪些符号能用来触发补全功能。YouCompleteMe 会调用它们各自 TypeScript 插件来进行语义查询。 ```text if !exists("g:ycm_semantic_triggers") @@ -63,7 +63,7 @@ let g:ycm_semantic_triggers['typescript'] = ['.'] ## Visual Studio 2013/2015 -[Visual Studio](https://www.visualstudio.com/)里安装Microsoft Web Tools时就带了TypeScript。 +[Visual Studio](https://www.visualstudio.com/)里安装 Microsoft Web Tools 时就带了 TypeScript。 TypeScript for Visual Studio 2015 在[这里](http://www.microsoft.com/en-us/download/details.aspx?id=48593) @@ -71,9 +71,8 @@ TypeScript for Visual Studio 2013 在[这里](https://www.microsoft.com/en-us/do ## Visual Studio Code -[Visual Studio Code](https://code.visualstudio.com/),是一个轻量级的跨平台编辑器,内置了对TypeScript的支持。 +[Visual Studio Code](https://code.visualstudio.com/),是一个轻量级的跨平台编辑器,内置了对 TypeScript 的支持。 ## Webstorm -[WebStorm](https://www.jetbrains.com/webstorm/),同其它JetBrains IDEs一样,直接包含了对TypeScript的支持。 - +[WebStorm](https://www.jetbrains.com/webstorm/),同其它 JetBrains IDEs 一样,直接包含了对 TypeScript 的支持。 diff --git a/zh/wiki/using-typescript-with-asp.net-5.md b/zh/wiki/using-typescript-with-asp.net-5.md index 6fa8f477..87569adb 100644 --- a/zh/wiki/using-typescript-with-asp.net-5.md +++ b/zh/wiki/using-typescript-with-asp.net-5.md @@ -1,18 +1,18 @@ -# 结合ASP.NET v5使用TypeScript +# 结合 ASP.NET v5 使用 TypeScript -与ASP.NET v5一起使用TypeScript需要你用特定的方式来设置你的工程。 更多关于ASP.NET v5的详细信息请查看[ASP.NET v5 文档](http://docs.asp.net/en/latest/conceptual-overview/index.html) 在Visual Studio的工程里支持当前的tsconfig.json还在开发之中,可以在这里查看进度[\#3983](https://github.com/Microsoft/TypeScript/issues/3983)。 +与 ASP.NET v5 一起使用 TypeScript 需要你用特定的方式来设置你的工程。 更多关于 ASP.NET v5 的详细信息请查看[ASP.NET v5 文档](http://docs.asp.net/en/latest/conceptual-overview/index.html) 在 Visual Studio 的工程里支持当前的 tsconfig.json 还在开发之中,可以在这里查看进度[\#3983](https://github.com/Microsoft/TypeScript/issues/3983)。 ## 工程设置 -我们就以在Visual Studio 2015里创建一个空的ASP.NET v5工程开始,如果你对ASP.NET v5还不熟悉,可以查看[这个教程](http://docs.asp.net/en/latest/tutorials/your-first-aspnet-application.html)。 +我们就以在 Visual Studio 2015 里创建一个空的 ASP.NET v5 工程开始,如果你对 ASP.NET v5 还不熟悉,可以查看[这个教程](http://docs.asp.net/en/latest/tutorials/your-first-aspnet-application.html)。 -![新创建一个空的工程](https://raw.githubusercontent.com/wiki/Microsoft/TypeScript/aspnet-screenshots/new-project.png) +![新创建一个空的工程](https://raw.githubusercontent.com/wiki/Microsoft/TypeScript/aspnet-screenshots/new-project.png) -然后在工程根目录下添加一个`scripts`目录。 这就是我们将要添加TypeScript文件和[`tsconfig.json`](../project-config/tsconfig.json.md)文件来设置编译选项的地方。 请注意目录名和路径都必须这样才能正常工作。 添加`tsconfig.json`文件,右键点击`scripts`目录,选择`Add`,`New Item`。 在`Client-side`下,你能够找到它,如下所示。 +然后在工程根目录下添加一个`scripts`目录。 这就是我们将要添加 TypeScript 文件和[`tsconfig.json`](../project-config/tsconfig.json.md)文件来设置编译选项的地方。 请注意目录名和路径都必须这样才能正常工作。 添加`tsconfig.json`文件,右键点击`scripts`目录,选择`Add`,`New Item`。 在`Client-side`下,你能够找到它,如下所示。 -![在 Visual Studio 里添加'tsconfig.json'文件](https://raw.githubusercontent.com/wiki/Microsoft/TypeScript/aspnet-screenshots/add-tsconfig.png) +![在 Visual Studio 里添加'tsconfig.json'文件](https://raw.githubusercontent.com/wiki/Microsoft/TypeScript/aspnet-screenshots/add-tsconfig.png) -![A project in Visual Studio's Solution Explorer](https://raw.githubusercontent.com/wiki/Microsoft/TypeScript/aspnet-screenshots/project.png) +![A project in Visual Studio's Solution Explorer](https://raw.githubusercontent.com/wiki/Microsoft/TypeScript/aspnet-screenshots/project.png) 最后我们还要将下面的选项添加到`tsconfig.json`文件的`"compilerOptions"`节点里,让编译器输出重定向到`wwwroot`文件夹: @@ -37,17 +37,16 @@ 现在如果我们构建这个工程,你就会注意到`app.js`和`app.js.map`文件被创建在`wwwroot`目录里。 -![构建后的文件](https://raw.githubusercontent.com/wiki/Microsoft/TypeScript/aspnet-screenshots/postbuild.png) +![构建后的文件](https://raw.githubusercontent.com/wiki/Microsoft/TypeScript/aspnet-screenshots/postbuild.png) ### 工程与虚拟工程 -当添加了一个`tsconfig.json`文件,你要明白很重要的一点是我们创建了一个虚拟TypeScript工程,在包含`tsconfig.json`文件的目录下。 被当作这个虚拟工程一部分的TypeScript文件是不会在保存的时候编译的。 在包含`tsconfig.json`文件的目录_外层_里存在的TypeScript文件_不会_被当作虚拟工程的一部分。 下图中,可以见到这个虚拟工程,在红色矩形里。 +当添加了一个`tsconfig.json`文件,你要明白很重要的一点是我们创建了一个虚拟 TypeScript 工程,在包含`tsconfig.json`文件的目录下。 被当作这个虚拟工程一部分的 TypeScript 文件是不会在保存的时候编译的。 在包含`tsconfig.json`文件的目录*外层*里存在的 TypeScript 文件*不会*被当作虚拟工程的一部分。 下图中,可以见到这个虚拟工程,在红色矩形里。 -![A virtual project in Visual Studio's Solution Explorer](https://raw.githubusercontent.com/wiki/Microsoft/TypeScript/aspnet-screenshots/virtual-project.png) +![A virtual project in Visual Studio's Solution Explorer](https://raw.githubusercontent.com/wiki/Microsoft/TypeScript/aspnet-screenshots/virtual-project.png) ### 保存时编译 -想要启用ASP.NET v5项目的_保存时编译_功能,你必须为不是虚拟TypeScript工程一部分的TypeScript文件启用_保存时编译_功能。 如果工程里存在`tsconfig.json`文件,那么模块类型选项的设置会被忽略。 +想要启用 ASP.NET v5 项目的*保存时编译*功能,你必须为不是虚拟 TypeScript 工程一部分的 TypeScript 文件启用*保存时编译*功能。 如果工程里存在`tsconfig.json`文件,那么模块类型选项的设置会被忽略。 ![Compile on Save](https://raw.githubusercontent.com/wiki/Microsoft/TypeScript/aspnet-screenshots/compile-on-save.png) -