├── .editorconfig ├── .eslintignore ├── .gitbook.yml ├── .gitbook └── assets │ ├── eslint-or-tslint-process.png │ ├── typescript-eslint-tslint.png │ ├── vscode-eslint-error.png │ └── vscode-output-eslint.png ├── .github └── FUNDING.yml ├── .gitignore ├── .lintmdrc ├── .prettierignore ├── .travis.yml ├── .vscode └── settings.json ├── README.md ├── SUMMARY.md ├── advanced ├── README.md ├── class-and-interfaces.md ├── class.md ├── declaration-merging.md ├── enum.md ├── further-reading.md ├── generics.md ├── string-literal-types.md ├── tuple.md └── type-aliases.md ├── assets ├── graphs.pptx ├── typescript-eslint.png └── wechat.jpeg ├── basics ├── README.md ├── any.md ├── built-in-objects.md ├── declaration-files.md ├── primitive-data-types.md ├── type-assertion.md ├── type-inference.md ├── type-of-array.md ├── type-of-function.md ├── type-of-object-interfaces.md └── union-types.md ├── engineering ├── README.md └── lint.md ├── examples ├── declaration-files │ ├── 01-jquery │ │ ├── src │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── 02-declare-var │ │ ├── src │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── 03-jquery-d-ts │ │ ├── src │ │ │ ├── index.ts │ │ │ └── jQuery.d.ts │ │ └── tsconfig.json │ ├── 04-declare-const-jquery │ │ ├── src │ │ │ ├── index.ts │ │ │ └── jQuery.d.ts │ │ └── tsconfig.json │ ├── 05-declare-jquery-value │ │ ├── src │ │ │ └── jQuery.d.ts │ │ └── tsconfig.json │ ├── 06-declare-function │ │ ├── src │ │ │ ├── index.ts │ │ │ └── jQuery.d.ts │ │ └── tsconfig.json │ ├── 07-declare-class │ │ ├── src │ │ │ ├── Animal.d.ts │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── 08-declare-enum │ │ ├── src │ │ │ ├── Directions.d.ts │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── 09-declare-namespace │ │ ├── src │ │ │ ├── index.ts │ │ │ └── jQuery.d.ts │ │ └── tsconfig.json │ ├── 10-declare-namespace-nesting │ │ ├── src │ │ │ ├── index.ts │ │ │ └── jQuery.d.ts │ │ └── tsconfig.json │ ├── 11-declare-namespace-dot │ │ ├── src │ │ │ ├── index.ts │ │ │ └── jQuery.d.ts │ │ └── tsconfig.json │ ├── 12-interface │ │ ├── src │ │ │ ├── index.ts │ │ │ └── jQuery.d.ts │ │ └── tsconfig.json │ ├── 13-avoid-name-conflict │ │ ├── src │ │ │ ├── index.ts │ │ │ └── jQuery.d.ts │ │ └── tsconfig.json │ ├── 14-declaration-merging │ │ ├── src │ │ │ ├── index.ts │ │ │ └── jQuery.d.ts │ │ └── tsconfig.json │ ├── 15-export │ │ ├── src │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── types │ │ │ └── foo │ │ │ └── index.d.ts │ ├── 16-declare-and-export │ │ ├── src │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── types │ │ │ └── foo │ │ │ └── index.d.ts │ ├── 17-export-namespace │ │ ├── src │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── types │ │ │ └── foo │ │ │ └── index.d.ts │ ├── 18-export-default │ │ ├── src │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── types │ │ │ └── foo │ │ │ └── index.d.ts │ ├── 19-export-default-enum-error │ │ ├── src │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── types │ │ │ └── foo │ │ │ └── index.d.ts │ ├── 20-export-default-enum │ │ ├── src │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── types │ │ │ └── foo │ │ │ └── index.d.ts │ ├── 21-export-equal │ │ ├── src │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── types │ │ │ └── foo │ │ │ └── index.d.ts │ ├── 22-export-as-namespace │ │ ├── src │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── types │ │ │ └── foo │ │ │ └── index.d.ts │ ├── 23-merge-global-interface │ │ ├── src │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── 24-merge-global-namespace │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── src │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── types │ │ │ └── jquery-plugin │ │ │ └── index.d.ts │ ├── 25-declare-global │ │ ├── src │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── types │ │ │ └── foo │ │ │ └── index.d.ts │ ├── 26-declare-module │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── src │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── types │ │ │ └── moment-plugin │ │ │ └── index.d.ts │ ├── 27-multiple-declare-module │ │ ├── src │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── types │ │ │ └── foo-bar.d.ts │ ├── 28-triple-slash-directives │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── src │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── types │ │ │ └── jquery-plugin │ │ │ └── index.d.ts │ ├── 29-triple-slash-directives-global │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── src │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── types │ │ │ └── node-plugin │ │ │ └── index.d.ts │ └── 30-auto-d-ts │ │ ├── lib │ │ ├── bar │ │ │ ├── index.d.ts │ │ │ └── index.js │ │ ├── index.d.ts │ │ └── index.js │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── src │ │ ├── bar │ │ │ └── index.ts │ │ └── index.ts │ │ └── tsconfig.json ├── eslint │ ├── .eslintrc.js │ └── index.ts ├── test.js └── test.ts ├── introduction ├── README.md ├── get-typescript.md ├── hello-typescript.md └── what-is-typescript.md ├── package-lock.json ├── package.json ├── prettier.config.js ├── thanks.md └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | 11 | # Matches multiple files with brace expansion notation 12 | # Set default charset 13 | [*] 14 | charset = utf-8 15 | 16 | # 4 space indentation 17 | # [*] 18 | # indent_style = space 19 | # indent_size = 4 20 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | _book 2 | -------------------------------------------------------------------------------- /.gitbook.yml: -------------------------------------------------------------------------------- 1 | root: ./ 2 | 3 | structure: 4 | readme: README.md 5 | summary: SUMMARY.md 6 | -------------------------------------------------------------------------------- /.gitbook/assets/eslint-or-tslint-process.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doggy8088/typescript-tutorial/87baecb9eab329de934b1d14bd3245f546503633/.gitbook/assets/eslint-or-tslint-process.png -------------------------------------------------------------------------------- /.gitbook/assets/typescript-eslint-tslint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doggy8088/typescript-tutorial/87baecb9eab329de934b1d14bd3245f546503633/.gitbook/assets/typescript-eslint-tslint.png -------------------------------------------------------------------------------- /.gitbook/assets/vscode-eslint-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doggy8088/typescript-tutorial/87baecb9eab329de934b1d14bd3245f546503633/.gitbook/assets/vscode-eslint-error.png -------------------------------------------------------------------------------- /.gitbook/assets/vscode-output-eslint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doggy8088/typescript-tutorial/87baecb9eab329de934b1d14bd3245f546503633/.gitbook/assets/vscode-output-eslint.png -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: xcatliu 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: https://github.com/xcatliu/buy-me-a-coffee 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | 4 | .DS_Store 5 | 6 | _book 7 | -------------------------------------------------------------------------------- /.lintmdrc: -------------------------------------------------------------------------------- 1 | { 2 | "excludeFiles": [], 3 | "rules": { 4 | "no-trailing-punctuation": 0, 5 | "no-long-code": 0 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | *.md 4 | 5 | _book 6 | assets 7 | 8 | .DS_Store 9 | .editorconfig 10 | .eslintignore 11 | .gitignore 12 | .lintmdrc 13 | .prettierignore 14 | 15 | package-lock.json 16 | 17 | examples/declaration-files/19-export-default-enum-error/types/foo/index.d.ts 18 | examples/**/*.js 19 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '10' 4 | install: 5 | - npm install 6 | script: 7 | - npm test 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.eol": "\n", 3 | "editor.tabSize": 4, 4 | "editor.formatOnSave": true, 5 | "typescript.tsdk": "node_modules/typescript/lib", 6 | "eslint.autoFixOnSave": false, 7 | "eslint.validate": [ 8 | "javascript", 9 | "javascriptreact", 10 | { 11 | "language": "vue", 12 | "autoFix": false 13 | }, 14 | { 15 | "language": "typescript", 16 | "autoFix": false 17 | }, 18 | { 19 | "language": "typescriptreact", 20 | "autoFix": false 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 前言 2 | 3 | 從 JavaScript 程式設計師的角度總結思考,循序漸進的理解 TypeScript。 4 | 5 | ## 正體中文翻譯 6 | 7 | 本書 [TypeScript 新手指南](https://willh.gitbook.io/typescript-tutorial/) 的原文來自於簡體中文的 [TypeScript 入门教程](https://github.com/xcatliu/typescript-tutorial/) 專案,並將其內容翻譯為正體中文版。如有正體中文翻譯不妥的地方,歡迎發 [Issue](https://github.com/doggy8088/typescript-tutorial/issues) 給我,或到 [Will 保哥的技術交流中心](https://www.facebook.com/will.fans/) 粉絲團留言給我,謝謝!🙂 8 | 9 | ## 關於本書 10 | 11 | * [線上閱讀](https://ts.xcatliu.com/)(部署在 [GitBook](https://www.gitbook.com/book/xcatliu/typescript-tutorial/details) 上,可能需要翻牆) 12 | * [線上閱讀(GitHub 版)](https://github.com/xcatliu/typescript-tutorial/blob/master/README.md) 13 | * [GitHub 地址](https://github.com/xcatliu/typescript-tutorial) 14 | * 作者:[xcatliu](https://github.com/xcatliu/) 15 | * 官方 QQ 群:[加入 QQ 群 767142358](https://jq.qq.com/?_wv=1027&k=5nkkFCl) 16 | 17 | 本書是作者在學習 [TypeScript](http://www.typescriptlang.org/) 後整理的學習筆記。 18 | 19 | 隨著對 TypeScript 理解的加深和 TypeScript 社群的發展,本書也會做出相應的更新,歡迎大家 [Star 收藏](https://github.com/xcatliu/typescript-tutorial)。 20 | 21 | * 發現文章內容有問題,可以直接在頁面下方評論 22 | * 對專案的建議,可以[提交 issue](https://github.com/xcatliu/typescript-tutorial/issues/new) 向作者反饋 23 | * 歡迎直接提交 pull-request 參與貢獻 24 | 25 | ## 為什麼要寫本書 26 | 27 | TypeScript 雖然有[官方手冊](http://www.typescriptlang.org/docs/handbook/basic-types.html)及其[非官方中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/),但是它每一章都希望能詳盡的描述一個概念,導致前面的章節就會包含很多後面才會學習到的內容,而有些本該一開始就瞭解的基礎知識卻在後面才會涉及。如果是初學者,可能需要閱讀多次才能理解。所以它更適合用來查閱,而不是學習。 28 | 29 | 與官方手冊不同,本書著重於從 JavaScript 程式設計師的角度總結思考,循序漸進的理解 TypeScript,希望能給大家一些幫助和啟示。 30 | 31 | 由於一些知識點與官方手冊重合度很高,本書會在相應章節推薦直接閱讀中文手冊。 32 | 33 | ## 關於 TypeScript 34 | 35 | [TypeScript](http://www.typescriptlang.org/) 是 JavaScript 的一個超集,主要提供了**型別系統**和**對 ES6 的支援**,它由 Microsoft 開發,程式碼[開源於 GitHub](https://github.com/Microsoft/TypeScript) 上。 36 | 37 | 它的第一個版本釋出於 2012 年 10 月,經歷了多次更新後,現在已成為前端社群中不可忽視的力量,不僅在 Microsoft 內部得到廣泛運用,而且 Google 的 [Angular2](https://angular.io/) 也使用了 TypeScript 作為開發語言。 38 | 39 | ## 適合人群 40 | 41 | 本書適合以下人群 42 | 43 | * 熟悉 JavaScript,至少閱讀過一遍[《JavaScript 高階程式設計》](https://book.douban.com/subject/10546125/) 44 | * 瞭解 ES6,推薦閱讀 [ECMAScript 6 入門](http://es6.ruanyifeng.com/) 45 | * 瞭解 Node.js,會用 npm 安裝及使用一些工具 46 | * 想了解 TypeScript 或者想對 TypeScript 有更深的理解 47 | 48 | 本書**不適合**以下人群 49 | 50 | * 沒有系統學習過 JavaScript 51 | * 已經能夠很熟練的運用 TypeScript 52 | 53 | ## 評價 54 | 55 | > 《TypeScript 入門課程》全面介紹了 TS 強大的型別系統,完整而簡潔,示例豐富,比官方文件更易讀,非常適合作為初學者學習 TS 的第一本書。 56 | > 57 | > —— [阮一峰](https://github.com/ruanyf) 58 | 59 | ## 目錄 60 | 61 | * [前言](./) 62 | * [簡介](introduction/) 63 | * [什麼是 TypeScript](introduction/what-is-typescript.md) 64 | * [安裝 TypeScript](introduction/get-typescript.md) 65 | * [Hello TypeScript](introduction/hello-typescript.md) 66 | * [基礎](basics/) 67 | * [原始資料型別](basics/primitive-data-types.md) 68 | * [任意值](basics/any.md) 69 | * [型別推論](basics/type-inference.md) 70 | * [聯合型別](basics/union-types.md) 71 | * [物件的型別——介面](basics/type-of-object-interfaces.md) 72 | * [陣列的型別](basics/type-of-array.md) 73 | * [函式的型別](basics/type-of-function.md) 74 | * [型別斷言](basics/type-assertion.md) 75 | * [宣告檔案](basics/declaration-files.md) 76 | * [內建物件](basics/built-in-objects.md) 77 | * [進階](advanced/) 78 | * [型別別名](advanced/type-aliases.md) 79 | * [字串字面量型別](advanced/string-literal-types.md) 80 | * [元組](advanced/tuple.md) 81 | * [列舉](advanced/enum.md) 82 | * [類別](advanced/class.md) 83 | * [類別與介面](advanced/class-and-interfaces.md) 84 | * [泛型](advanced/generics.md) 85 | * [宣告合併](advanced/declaration-merging.md) 86 | * [擴充套件閱讀](advanced/further-reading.md) 87 | * [工程](engineering/) 88 | * [程式碼檢查](engineering/lint.md) 89 | * [感謝](thanks.md) 90 | 91 | ## 版權許可 92 | 93 | 本書採用「保持署名—非商用」創意共享 4.0 許可證。 94 | 95 | 只要保持原作者署名和非商用,您可以自由地閱讀、分享、修改本書。 96 | 97 | 詳細的法律條文請參見[創意共享](http://creativecommons.org/licenses/by-nc/4.0/)網站。 98 | 99 | ## 相關資料 100 | 101 | * [TypeScript 官網](http://www.typescriptlang.org/) 102 | * [Handbook](http://www.typescriptlang.org/docs/handbook/basic-types.html)([中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/)) 103 | * [ECMAScript 6 入門](http://es6.ruanyifeng.com/) 104 | * [下一章:簡介](introduction/) 105 | 106 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [前言](README.md) 4 | * [簡介](introduction/README.md) 5 | * [什麼是 TypeScript](introduction/what-is-typescript.md) 6 | * [安裝 TypeScript](introduction/get-typescript.md) 7 | * [Hello TypeScript](introduction/hello-typescript.md) 8 | * [基礎](basics/README.md) 9 | * [原始資料型別](basics/primitive-data-types.md) 10 | * [任意值](basics/any.md) 11 | * [型別推論](basics/type-inference.md) 12 | * [聯合型別](basics/union-types.md) 13 | * [物件的型別——介面](basics/type-of-object-interfaces.md) 14 | * [陣列的型別](basics/type-of-array.md) 15 | * [函式的型別](basics/type-of-function.md) 16 | * [型別斷言](basics/type-assertion.md) 17 | * [宣告檔案](basics/declaration-files.md) 18 | * [內建物件](basics/built-in-objects.md) 19 | * [進階](advanced/README.md) 20 | * [型別別名](advanced/type-aliases.md) 21 | * [字串字面量型別](advanced/string-literal-types.md) 22 | * [元組](advanced/tuple.md) 23 | * [列舉](advanced/enum.md) 24 | * [類別](advanced/class.md) 25 | * [類別與介面](advanced/class-and-interfaces.md) 26 | * [泛型](advanced/generics.md) 27 | * [宣告合併](advanced/declaration-merging.md) 28 | * [延伸閱讀](advanced/further-reading.md) 29 | * [工程](engineering/README.md) 30 | * [程式碼檢查](engineering/lint.md) 31 | * [感謝](thanks.md) 32 | 33 | -------------------------------------------------------------------------------- /advanced/README.md: -------------------------------------------------------------------------------- 1 | # 進階 2 | 3 | 本部分介紹一些進階的型別與技術,具體內容包括: 4 | 5 | * [型別別名](type-aliases.md) 6 | * [字串字面量型別](string-literal-types.md) 7 | * [元組](tuple.md) 8 | * [列舉](enum.md) 9 | * [類別](class.md) 10 | * [類別與介面](class-and-interfaces.md) 11 | * [泛型](generics.md) 12 | * [宣告合併](declaration-merging.md) 13 | * [擴充套件閱讀](further-reading.md) 14 | * [上一章:內建物件](https://github.com/doggy8088/typescript-tutorial/tree/3e6aae02f8c2fc9db956b346556647eb1d3cdbe3/advanced/built-in-objects.md) 15 | * [下一章:型別別名](type-aliases.md) 16 | 17 | -------------------------------------------------------------------------------- /advanced/class-and-interfaces.md: -------------------------------------------------------------------------------- 1 | # 類別與介面 2 | 3 | [之前學習過](../basics/type-of-object-interfaces.md),介面(Interfaces)可以用於對「物件的形狀(Shape)」進行描述。 4 | 5 | 這一章主要介紹介面的另一個用途,對類別的一部分行為進行抽象。 6 | 7 | ## 類別實現介面 8 | 9 | 實現(implements)是面向物件中的一個重要概念。一般來講,一個類別只能繼承自另一個類別,有時候不同類別之間可以有一些共有的特性,這時候就可以把特性提取成介面(interfaces),用 `implements` 關鍵字來實現。這個特性大大提高了面向物件的靈活性。 10 | 11 | 舉例來說,門是一個類別,防盜門是門的子類別。如果防盜門有一個報警器的功能,我們可以簡單的給防盜門新增一個報警方法。這時候如果有另一個類別,車,也有報警器的功能,就可以考慮把報警器提取出來,作為一個介面,防盜門和車都去實現它: 12 | 13 | ```typescript 14 | interface Alarm { 15 | alert(); 16 | } 17 | 18 | class Door { 19 | } 20 | 21 | class SecurityDoor extends Door implements Alarm { 22 | alert() { 23 | console.log('SecurityDoor alert'); 24 | } 25 | } 26 | 27 | class Car implements Alarm { 28 | alert() { 29 | console.log('Car alert'); 30 | } 31 | } 32 | ``` 33 | 34 | 一個類別可以實現多個介面: 35 | 36 | ```typescript 37 | interface Alarm { 38 | alert(); 39 | } 40 | 41 | interface Light { 42 | lightOn(); 43 | lightOff(); 44 | } 45 | 46 | class Car implements Alarm, Light { 47 | alert() { 48 | console.log('Car alert'); 49 | } 50 | lightOn() { 51 | console.log('Car light on'); 52 | } 53 | lightOff() { 54 | console.log('Car light off'); 55 | } 56 | } 57 | ``` 58 | 59 | 上例中,`Car` 實現了 `Alarm` 和 `Light` 介面,既能報警,也能開關車燈。 60 | 61 | ## 介面繼承介面 62 | 63 | 介面與介面之間可以是繼承關係: 64 | 65 | ```typescript 66 | interface Alarm { 67 | alert(); 68 | } 69 | 70 | interface LightableAlarm extends Alarm { 71 | lightOn(); 72 | lightOff(); 73 | } 74 | ``` 75 | 76 | 上例中,我們使用 `extends` 使 `LightableAlarm` 繼承 `Alarm`。 77 | 78 | ## 介面繼承類別 79 | 80 | 介面也可以繼承類別: 81 | 82 | ```typescript 83 | class Point { 84 | x: number; 85 | y: number; 86 | } 87 | 88 | interface Point3d extends Point { 89 | z: number; 90 | } 91 | 92 | let point3d: Point3d = {x: 1, y: 2, z: 3}; 93 | ``` 94 | 95 | ## 混合型別 96 | 97 | [之前學習過](../basics/type-of-function.md#介面中函式的定義),可以使用介面的方式來定義一個函式需要符合的形狀: 98 | 99 | ```typescript 100 | interface SearchFunc { 101 | (source: string, subString: string): boolean; 102 | } 103 | 104 | let mySearch: SearchFunc; 105 | mySearch = function(source: string, subString: string) { 106 | return source.search(subString) !== -1; 107 | } 108 | ``` 109 | 110 | 有時候,一個函式還可以有自己的屬性和方法: 111 | 112 | ```typescript 113 | interface Counter { 114 | (start: number): string; 115 | interval: number; 116 | reset(): void; 117 | } 118 | 119 | function getCounter(): Counter { 120 | let counter = function (start: number) { }; 121 | counter.interval = 123; 122 | counter.reset = function () { }; 123 | return counter; 124 | } 125 | 126 | let c = getCounter(); 127 | c(10); 128 | c.reset(); 129 | c.interval = 5.0; 130 | ``` 131 | 132 | ## 參考 133 | 134 | * [Interfaces](http://www.typescriptlang.org/docs/handbook/interfaces.html)([中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Interfaces.html)) 135 | * [上一章:類別](class.md) 136 | * [下一章:泛型](generics.md) 137 | 138 | -------------------------------------------------------------------------------- /advanced/class.md: -------------------------------------------------------------------------------- 1 | # 類別 2 | 3 | 傳統方法中,JavaScript 透過建構函式實現類別的概念,透過原型鏈實現繼承。而在 ES6 中,我們終於迎來了 `class`。 4 | 5 | TypeScript 除了實現了所有 ES6 中的類別的功能以外,還添加了一些新的用法。 6 | 7 | 這一節主要介紹類別的用法,下一節再介紹如何定義類別的型別。 8 | 9 | ## 類別的概念 10 | 11 | 雖然 JavaScript 中有類別的概念,但是可能大多數 JavaScript 程式設計師並不是非常熟悉類別,這裡對類別相關的概念做一個簡單的介紹。 12 | 13 | * 類別\(Class\):定義了一件事物的抽象特點,包含它的屬性和方法 14 | * 物件(Object):類別的實例,透過 `new` 產生 15 | * 面向物件(OOP)的三大特性:封裝、繼承、多型 16 | * 封裝(Encapsulation):將對資料的操作細節隱藏起來,只暴露對外的介面。外界呼叫端不需要(也不可能)知道細節,就能透過對外提供的介面來訪問該物件,同時也保證了外界無法任意更改物件內部的資料 17 | * 繼承(Inheritance):子類別繼承父類別,子類別除了擁有父類別的所有特性外,還有一些更具體的特性 18 | * 多型(Polymorphism):由繼承而產生了相關的不同的類別,對同一個方法可以有不同的響應。比如 `Cat` 和 `Dog` 都繼承自 `Animal`,但是分別實現了自己的 `eat` 方法。此時針對某一個實例,我們無需瞭解它是 `Cat` 還是 `Dog`,就可以直接呼叫 `eat` 方法,程式會自動判斷出來應該如何執行 `eat` 19 | * 存取器(getter & setter):用以改變屬性的讀取和賦值行為 20 | * 修飾符(Modifiers):修飾符是一些關鍵字,用於限定成員或型別的性質。比如 `public` 表示公有屬性或方法 21 | * 抽象類別(Abstract Class):抽象類別是供其他類別繼承的基底類別,抽象類別不允許被實例化。抽象類別中的抽象方法必須在子類別中被實現 22 | * 介面(Interfaces):不同類別之間公有的屬性或方法,可以抽象成一個介面。介面可以被類別實現(implements)。一個類別只能繼承自另一個類別,但是可以實現多個介面 23 | 24 | ## ES6 中類別的用法 25 | 26 | 下面我們先回顧一下 ES6 中類別的用法,更詳細的介紹可以參考 [ECMAScript 6 入門 - Class](http://es6.ruanyifeng.com/#docs/class)。 27 | 28 | ### 屬性和方法 29 | 30 | 使用 `class` 定義類別,使用 `constructor` 定義建構函式。 31 | 32 | 透過 `new` 產生新實例的時候,會自動呼叫建構函式。 33 | 34 | ```javascript 35 | class Animal { 36 | constructor(name) { 37 | this.name = name; 38 | } 39 | sayHi() { 40 | return `My name is ${this.name}`; 41 | } 42 | } 43 | 44 | let a = new Animal('Jack'); 45 | console.log(a.sayHi()); // My name is Jack 46 | ``` 47 | 48 | ### 類別的繼承 49 | 50 | 使用 `extends` 關鍵字實現繼承,子類別中使用 `super` 關鍵字來呼叫父類別的建構函式和方法。 51 | 52 | ```javascript 53 | class Cat extends Animal { 54 | constructor(name) { 55 | super(name); // 呼叫父類別的 constructor(name) 56 | console.log(this.name); 57 | } 58 | sayHi() { 59 | return 'Meow, ' + super.sayHi(); // 呼叫父類別的 sayHi() 60 | } 61 | } 62 | 63 | let c = new Cat('Tom'); // Tom 64 | console.log(c.sayHi()); // Meow, My name is Tom 65 | ``` 66 | 67 | ### 存取器 68 | 69 | 使用 getter 和 setter 可以改變屬性的賦值和讀取行為: 70 | 71 | ```javascript 72 | class Animal { 73 | constructor(name) { 74 | this._name = name; 75 | } 76 | get name() { 77 | return this._name; 78 | } 79 | set name(value) { 80 | this._name = value 81 | console.log('setter: ' + value); 82 | } 83 | } 84 | 85 | let a = new Animal('Jack'); 86 | a.name = 'Tom'; // setter: Tom 87 | console.log(a.name); // Tom 88 | ``` 89 | 90 | ### 靜態方法 91 | 92 | 使用 `static` 修飾符修飾的方法稱為靜態方法,它們不需要實例化,而是直接透過類別來呼叫: 93 | 94 | ```javascript 95 | class Animal { 96 | static isAnimal(a) { 97 | return a instanceof Animal; 98 | } 99 | } 100 | 101 | let a = new Animal('Jack'); 102 | Animal.isAnimal(a); // true 103 | a.isAnimal(a); // TypeError: a.isAnimal is not a function 104 | ``` 105 | 106 | ## ES7 中類別的用法 107 | 108 | ES7 中有一些關於類別的提案,TypeScript 也實現了它們,這裡做一個簡單的介紹。 109 | 110 | ### 實例屬性 111 | 112 | ES6 中實例的屬性只能透過建構函式中的 `this.xxx` 來定義,ES7 提案中可以直接在類別裡面定義: 113 | 114 | ```javascript 115 | class Animal { 116 | name = 'Jack'; 117 | 118 | constructor() { 119 | // ... 120 | } 121 | } 122 | 123 | let a = new Animal(); 124 | console.log(a.name); // Jack 125 | ``` 126 | 127 | ### 靜態屬性 128 | 129 | ES7 提案中,可以使用 `static` 定義一個靜態屬性: 130 | 131 | ```javascript 132 | class Animal { 133 | static num = 42; 134 | 135 | constructor() { 136 | // ... 137 | } 138 | } 139 | 140 | console.log(Animal.num); // 42 141 | ``` 142 | 143 | ## TypeScript 中類別的用法 144 | 145 | ### public private 和 protected 146 | 147 | TypeScript 可以使用三種訪問修飾符(Access Modifiers),分別是 `public`、`private` 和 `protected`。 148 | 149 | * `public` 修飾的屬性或方法是公有的,可以在任何地方被訪問到,預設所有的屬性和方法都是 `public` 的 150 | * `private` 修飾的屬性或方法是私有的,不能在宣告它的類別的外部訪問 151 | * `protected` 修飾的屬性或方法是受保護的,它和 `private` 類似,區別是它在子類別中也是允許被訪問的 152 | 153 | 下面舉一些例子: 154 | 155 | ```typescript 156 | class Animal { 157 | public name; 158 | public constructor(name) { 159 | this.name = name; 160 | } 161 | } 162 | 163 | let a = new Animal('Jack'); 164 | console.log(a.name); // Jack 165 | a.name = 'Tom'; 166 | console.log(a.name); // Tom 167 | ``` 168 | 169 | 上面的例子中,`name` 被設定為了 `public`,所以直接訪問實例的 `name` 屬性是允許的。 170 | 171 | 很多時候,我們希望有的屬性是無法直接存取的,這時候就可以用 `private` 了: 172 | 173 | ```typescript 174 | class Animal { 175 | private name; 176 | public constructor(name) { 177 | this.name = name; 178 | } 179 | } 180 | 181 | let a = new Animal('Jack'); 182 | console.log(a.name); // Jack 183 | a.name = 'Tom'; 184 | 185 | // index.ts(9,13): error TS2341: Property 'name' is private and only accessible within class 'Animal'. 186 | // index.ts(10,1): error TS2341: Property 'name' is private and only accessible within class 'Animal'. 187 | ``` 188 | 189 | 需要注意的是,TypeScript 編譯之後的程式碼中,並沒有限制 `private` 屬性在外部的可及性。 190 | 191 | 上面的例子編譯後的程式碼是: 192 | 193 | ```javascript 194 | var Animal = (function () { 195 | function Animal(name) { 196 | this.name = name; 197 | } 198 | return Animal; 199 | }()); 200 | var a = new Animal('Jack'); 201 | console.log(a.name); 202 | a.name = 'Tom'; 203 | ``` 204 | 205 | 使用 `private` 修飾的屬性或方法,在子類別中也是不允許訪問的: 206 | 207 | ```typescript 208 | class Animal { 209 | private name; 210 | public constructor(name) { 211 | this.name = name; 212 | } 213 | } 214 | 215 | class Cat extends Animal { 216 | constructor(name) { 217 | super(name); 218 | console.log(this.name); 219 | } 220 | } 221 | 222 | // index.ts(11,17): error TS2341: Property 'name' is private and only accessible within class 'Animal'. 223 | ``` 224 | 225 | 而如果是用 `protected` 修飾,則允許在子類別中訪問: 226 | 227 | ```typescript 228 | class Animal { 229 | protected name; 230 | public constructor(name) { 231 | this.name = name; 232 | } 233 | } 234 | 235 | class Cat extends Animal { 236 | constructor(name) { 237 | super(name); 238 | console.log(this.name); 239 | } 240 | } 241 | ``` 242 | 243 | 當建構函式修飾為 `private` 時,該類別不允許被繼承或者實例化: 244 | 245 | ```typescript 246 | class Animal { 247 | public name; 248 | private constructor (name) { 249 | this.name = name; 250 | } 251 | } 252 | class Cat extends Animal { 253 | constructor (name) { 254 | super(name); 255 | } 256 | } 257 | 258 | let a = new Animal('Jack'); 259 | 260 | // index.ts(7,19): TS2675: Cannot extend a class 'Animal'. Class constructor is marked as private. 261 | // index.ts(13,9): TS2673: Constructor of class 'Animal' is private and only accessible within the class declaration. 262 | ``` 263 | 264 | 當建構函式修飾為 `protected` 時,該類別只允許被繼承: 265 | 266 | ```typescript 267 | class Animal { 268 | public name; 269 | protected constructor (name) { 270 | this.name = name; 271 | } 272 | } 273 | class Cat extends Animal { 274 | constructor (name) { 275 | super(name); 276 | } 277 | } 278 | 279 | let a = new Animal('Jack'); 280 | 281 | // index.ts(13,9): TS2674: Constructor of class 'Animal' is protected and only accessible within the class declaration. 282 | ``` 283 | 284 | 修飾符還可以使用在建構函式引數中,等同於類別中定義該屬性,使程式碼更簡潔。 285 | 286 | ```typescript 287 | class Animal { 288 | // public name: string; 289 | public constructor (public name) { 290 | this.name = name; 291 | } 292 | } 293 | ``` 294 | 295 | ### readonly 296 | 297 | 只讀屬性關鍵字,只允許出現在屬性宣告或索引簽名中。 298 | 299 | ```typescript 300 | class Animal { 301 | readonly name; 302 | public constructor(name) { 303 | this.name = name; 304 | } 305 | } 306 | 307 | let a = new Animal('Jack'); 308 | console.log(a.name); // Jack 309 | a.name = 'Tom'; 310 | 311 | // index.ts(10,3): TS2540: Cannot assign to 'name' because it is a read-only property. 312 | ``` 313 | 314 | 注意如果 `readonly` 和其他訪問修飾符同時存在的話,需要寫在其後面。 315 | 316 | ```typescript 317 | class Animal { 318 | // public readonly name; 319 | public constructor(public readonly name) { 320 | this.name = name; 321 | } 322 | } 323 | ``` 324 | 325 | ### 抽象類別 326 | 327 | `abstract` 用於定義抽象類別和其中的抽象方法。 328 | 329 | 什麼是抽象類別? 330 | 331 | 首先,抽象類別是不允許被實例化的: 332 | 333 | ```typescript 334 | abstract class Animal { 335 | public name; 336 | public constructor(name) { 337 | this.name = name; 338 | } 339 | public abstract sayHi(); 340 | } 341 | 342 | let a = new Animal('Jack'); 343 | 344 | // index.ts(9,11): error TS2511: Cannot create an instance of the abstract class 'Animal'. 345 | ``` 346 | 347 | 上面的例子中,我們定義了一個抽象類別 `Animal`,並且定義了一個抽象方法 `sayHi`。在實例化抽象類別的時候報錯了。 348 | 349 | 其次,抽象類別中的抽象方法必須被子類別實現: 350 | 351 | ```typescript 352 | abstract class Animal { 353 | public name; 354 | public constructor(name) { 355 | this.name = name; 356 | } 357 | public abstract sayHi(); 358 | } 359 | 360 | class Cat extends Animal { 361 | public eat() { 362 | console.log(`${this.name} is eating.`); 363 | } 364 | } 365 | 366 | let cat = new Cat('Tom'); 367 | 368 | // index.ts(9,7): error TS2515: Non-abstract class 'Cat' does not implement inherited abstract member 'sayHi' from class 'Animal'. 369 | ``` 370 | 371 | 上面的例子中,我們定義了一個類別 `Cat` 繼承了抽象類別 `Animal`,但是沒有實現抽象方法 `sayHi`,所以編譯報錯了。 372 | 373 | 下面是一個正確使用抽象類別的例子: 374 | 375 | ```typescript 376 | abstract class Animal { 377 | public name; 378 | public constructor(name) { 379 | this.name = name; 380 | } 381 | public abstract sayHi(); 382 | } 383 | 384 | class Cat extends Animal { 385 | public sayHi() { 386 | console.log(`Meow, My name is ${this.name}`); 387 | } 388 | } 389 | 390 | let cat = new Cat('Tom'); 391 | ``` 392 | 393 | 上面的例子中,我們實現了抽象方法 `sayHi`,編譯通過了。 394 | 395 | 需要注意的是,即使是抽象方法,TypeScript 的編譯結果中,仍然會存在這個類別,上面的程式碼的編譯結果是: 396 | 397 | ```javascript 398 | var __extends = (this && this.__extends) || function (d, b) { 399 | for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; 400 | function __() { this.constructor = d; } 401 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 402 | }; 403 | var Animal = (function () { 404 | function Animal(name) { 405 | this.name = name; 406 | } 407 | return Animal; 408 | }()); 409 | var Cat = (function (_super) { 410 | __extends(Cat, _super); 411 | function Cat() { 412 | _super.apply(this, arguments); 413 | } 414 | Cat.prototype.sayHi = function () { 415 | console.log('Meow, My name is ' + this.name); 416 | }; 417 | return Cat; 418 | }(Animal)); 419 | var cat = new Cat('Tom'); 420 | ``` 421 | 422 | ## 類別的型別 423 | 424 | 給類別加上 TypeScript 的型別很簡單,與介面類似: 425 | 426 | ```typescript 427 | class Animal { 428 | name: string; 429 | constructor(name: string) { 430 | this.name = name; 431 | } 432 | sayHi(): string { 433 | return `My name is ${this.name}`; 434 | } 435 | } 436 | 437 | let a: Animal = new Animal('Jack'); 438 | console.log(a.sayHi()); // My name is Jack 439 | ``` 440 | 441 | ## 參考 442 | 443 | * [Classes](http://www.typescriptlang.org/docs/handbook/classes.html)([中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Classes.html)) 444 | * [ECMAScript 6 入門 - Class](http://es6.ruanyifeng.com/#docs/class) 445 | * [上一章:列舉](enum.md) 446 | * [下一章:類別與介面](class-and-interfaces.md) 447 | 448 | -------------------------------------------------------------------------------- /advanced/declaration-merging.md: -------------------------------------------------------------------------------- 1 | # 宣告合併 2 | 3 | 如果定義了兩個相同名字的函式、介面或類別,那麼它們會合併成一個型別: 4 | 5 | ## 函式的合併 6 | 7 | [之前學習過](../basics/type-of-function.md#過載),我們可以使用過載定義多個函式型別: 8 | 9 | ```typescript 10 | function reverse(x: number): number; 11 | function reverse(x: string): string; 12 | function reverse(x: number | string): number | string { 13 | if (typeof x === 'number') { 14 | return Number(x.toString().split('').reverse().join('')); 15 | } else if (typeof x === 'string') { 16 | return x.split('').reverse().join(''); 17 | } 18 | } 19 | ``` 20 | 21 | ## 介面的合併 22 | 23 | 介面中的屬性在合併時會簡單的合併到一個介面中: 24 | 25 | ```typescript 26 | interface Alarm { 27 | price: number; 28 | } 29 | interface Alarm { 30 | weight: number; 31 | } 32 | ``` 33 | 34 | 相當於: 35 | 36 | ```typescript 37 | interface Alarm { 38 | price: number; 39 | weight: number; 40 | } 41 | ``` 42 | 43 | 注意,**合併的屬性的型別必須是唯一的**: 44 | 45 | ```typescript 46 | interface Alarm { 47 | price: number; 48 | } 49 | interface Alarm { 50 | price: number; // 雖然重複了,但是型別都是 `number`,所以不會報錯 51 | weight: number; 52 | } 53 | ``` 54 | 55 | ```typescript 56 | interface Alarm { 57 | price: number; 58 | } 59 | interface Alarm { 60 | price: string; // 型別不一致,會報錯 61 | weight: number; 62 | } 63 | 64 | // index.ts(5,3): error TS2403: Subsequent variable declarations must have the same type. Variable 'price' must be of type 'number', but here has type 'string'. 65 | ``` 66 | 67 | 介面中方法的合併,與函式的合併一樣: 68 | 69 | ```typescript 70 | interface Alarm { 71 | price: number; 72 | alert(s: string): string; 73 | } 74 | interface Alarm { 75 | weight: number; 76 | alert(s: string, n: number): string; 77 | } 78 | ``` 79 | 80 | 相當於: 81 | 82 | ```typescript 83 | interface Alarm { 84 | price: number; 85 | weight: number; 86 | alert(s: string): string; 87 | alert(s: string, n: number): string; 88 | } 89 | ``` 90 | 91 | ## 類別的合併 92 | 93 | ~~類別的合併與介面的合併規則一致。~~ 94 | 95 | 同名的類別會發生宣告衝突,無法直接合併。(原文的內容有誤,已發 [Issue](https://github.com/xcatliu/typescript-tutorial/issues/181) 要求更正) 96 | 97 | ## 參考 98 | 99 | * [Declaration Merging](http://www.typescriptlang.org/docs/handbook/declaration-merging.html)([中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Declaration%20Merging.html)) 100 | * [上一章:泛型](generics.md) 101 | * [下一章:擴充套件閱讀](further-reading.md) 102 | 103 | -------------------------------------------------------------------------------- /advanced/enum.md: -------------------------------------------------------------------------------- 1 | # 列舉 2 | 3 | 列舉(Enum)型別用於取值被限定在一定範圍內的場景,比如一週只能有七天,顏色限定為紅綠藍等。 4 | 5 | ## 簡單的例子 6 | 7 | 列舉使用 `enum` 關鍵字來定義: 8 | 9 | ```typescript 10 | enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat}; 11 | ``` 12 | 13 | 列舉成員會被賦值為從 `0` 開始遞增的數字,同時也會對列舉值到列舉名進行反向對映: 14 | 15 | ```typescript 16 | enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat}; 17 | 18 | console.log(Days["Sun"] === 0); // true 19 | console.log(Days["Mon"] === 1); // true 20 | console.log(Days["Tue"] === 2); // true 21 | console.log(Days["Sat"] === 6); // true 22 | 23 | console.log(Days[0] === "Sun"); // true 24 | console.log(Days[1] === "Mon"); // true 25 | console.log(Days[2] === "Tue"); // true 26 | console.log(Days[6] === "Sat"); // true 27 | ``` 28 | 29 | 事實上,上面的例子會被編譯為: 30 | 31 | ```javascript 32 | var Days; 33 | (function (Days) { 34 | Days[Days["Sun"] = 0] = "Sun"; 35 | Days[Days["Mon"] = 1] = "Mon"; 36 | Days[Days["Tue"] = 2] = "Tue"; 37 | Days[Days["Wed"] = 3] = "Wed"; 38 | Days[Days["Thu"] = 4] = "Thu"; 39 | Days[Days["Fri"] = 5] = "Fri"; 40 | Days[Days["Sat"] = 6] = "Sat"; 41 | })(Days || (Days = {})); 42 | ``` 43 | 44 | ## 手動賦值 45 | 46 | 我們也可以給列舉項手動賦值: 47 | 48 | ```typescript 49 | enum Days {Sun = 7, Mon = 1, Tue, Wed, Thu, Fri, Sat}; 50 | 51 | console.log(Days["Sun"] === 7); // true 52 | console.log(Days["Mon"] === 1); // true 53 | console.log(Days["Tue"] === 2); // true 54 | console.log(Days["Sat"] === 6); // true 55 | ``` 56 | 57 | 上面的例子中,未手動賦值的列舉項會接著上一個列舉項遞增。 58 | 59 | 如果未手動賦值的列舉項與手動賦值的重複了,TypeScript 是不會察覺到這一點的: 60 | 61 | ```typescript 62 | enum Days {Sun = 3, Mon = 1, Tue, Wed, Thu, Fri, Sat}; 63 | 64 | console.log(Days["Sun"] === 3); // true 65 | console.log(Days["Wed"] === 3); // true 66 | console.log(Days[3] === "Sun"); // false 67 | console.log(Days[3] === "Wed"); // true 68 | ``` 69 | 70 | 上面的例子中,遞增到 `3` 的時候與前面的 `Sun` 的取值重複了,但是 TypeScript 並沒有報錯,導致 `Days[3]` 的值先是 `"Sun"`,而後又被 `"Wed"` 覆蓋了。編譯的結果是: 71 | 72 | ```javascript 73 | var Days; 74 | (function (Days) { 75 | Days[Days["Sun"] = 3] = "Sun"; 76 | Days[Days["Mon"] = 1] = "Mon"; 77 | Days[Days["Tue"] = 2] = "Tue"; 78 | Days[Days["Wed"] = 3] = "Wed"; 79 | Days[Days["Thu"] = 4] = "Thu"; 80 | Days[Days["Fri"] = 5] = "Fri"; 81 | Days[Days["Sat"] = 6] = "Sat"; 82 | })(Days || (Days = {})); 83 | ``` 84 | 85 | 所以使用的時候需要注意,最好不要出現這種覆蓋的情況。 86 | 87 | 手動賦值的列舉項可以不是數字,此時需要使用型別斷言來讓 tsc 無視型別檢查 \(編譯出的 js 仍然是可用的\): 88 | 89 | ```typescript 90 | enum Days {Sun = 7, Mon, Tue, Wed, Thu, Fri, Sat = "S"}; 91 | ``` 92 | 93 | ```javascript 94 | var Days; 95 | (function (Days) { 96 | Days[Days["Sun"] = 7] = "Sun"; 97 | Days[Days["Mon"] = 8] = "Mon"; 98 | Days[Days["Tue"] = 9] = "Tue"; 99 | Days[Days["Wed"] = 10] = "Wed"; 100 | Days[Days["Thu"] = 11] = "Thu"; 101 | Days[Days["Fri"] = 12] = "Fri"; 102 | Days[Days["Sat"] = "S"] = "Sat"; 103 | })(Days || (Days = {})); 104 | ``` 105 | 106 | 當然,手動賦值的列舉項也可以為小數或負數,此時後續未手動賦值的項的遞增步長仍為 `1`: 107 | 108 | ```typescript 109 | enum Days {Sun = 7, Mon = 1.5, Tue, Wed, Thu, Fri, Sat}; 110 | 111 | console.log(Days["Sun"] === 7); // true 112 | console.log(Days["Mon"] === 1.5); // true 113 | console.log(Days["Tue"] === 2.5); // true 114 | console.log(Days["Sat"] === 6.5); // true 115 | ``` 116 | 117 | ## 常數項和計算所得項 118 | 119 | 列舉項有兩種型別:常數項(constant member)和計算所得項(computed member)。 120 | 121 | 前面我們所舉的例子都是常數項,一個典型的計算所得項的例子: 122 | 123 | ```typescript 124 | enum Color {Red, Green, Blue = "blue".length}; 125 | ``` 126 | 127 | 上面的例子中,`"blue".length` 就是一個計算所得項。 128 | 129 | 上面的例子不會報錯,但是**如果緊接在計算所得項後面的是未手動賦值的項,那麼它就會因為無法獲得初始值而報錯**: 130 | 131 | ```typescript 132 | enum Color {Red = "red".length, Green, Blue}; 133 | 134 | // index.ts(1,33): error TS1061: Enum member must have initializer. 135 | // index.ts(1,40): error TS1061: Enum member must have initializer. 136 | ``` 137 | 138 | 下面是常數項和計算所得項的完整定義,部分參考自[中文手冊 - 列舉](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Enums.html): 139 | 140 | 當滿足以下條件時,列舉成員被當作是常數: 141 | 142 | * 不具有初始化函式並且之前的列舉成員是常數。在這種情況下,當前列舉成員的值為上一個列舉成員的值加 `1`。但第一個列舉元素是個例外。如果它沒有初始化方法,那麼它的初始值為 `0`。 143 | * 列舉成員使用常數列舉表示式初始化。常數列舉表示式是 TypeScript 表示式的子集,它可以在編譯階段求值。當一個表示式滿足下面條件之一時,它就是一個常數列舉表示式: 144 | * 數字字面量 145 | * 參考之前定義的常數列舉成員(可以是在不同的列舉型別中定義的)如果這個成員是在同一個列舉型別中定義的,可以使用非限定名來參考 146 | * 帶括號的常數列舉表示式 147 | * `+`, `-`, `~` 一元運算子應用於常數列舉表示式 148 | * `+`, `-`, `*`, `/`, `%`, `<<`, `>>`, `>>>`, `&`, `|`, `^` 二元運算子,常數列舉表示式做為其一個操作物件。若常數列舉表示式求值後為 NaN 或 Infinity,則會在編譯階段報錯 149 | 150 | 所有其它情況的列舉成員被當作是需要計算得出的值。 151 | 152 | ## 常數列舉 153 | 154 | 常數列舉是使用 `const enum` 定義的列舉型別: 155 | 156 | ```typescript 157 | const enum Directions { 158 | Up, 159 | Down, 160 | Left, 161 | Right 162 | } 163 | 164 | let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right]; 165 | ``` 166 | 167 | 常數列舉與普通列舉的區別是,它會在編譯階段被刪除,並且不能包含計算成員。 168 | 169 | 上例的編譯結果是: 170 | 171 | ```javascript 172 | var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */]; 173 | ``` 174 | 175 | 假如包含了計算成員,則會在編譯階段報錯: 176 | 177 | ```typescript 178 | const enum Color {Red, Green, Blue = "blue".length}; 179 | 180 | // index.ts(1,38): error TS2474: In 'const' enum declarations member initializer must be constant expression. 181 | ``` 182 | 183 | ## 外部列舉 184 | 185 | 外部列舉(Ambient Enums)是使用 `declare enum` 定義的列舉型別: 186 | 187 | ```typescript 188 | declare enum Directions { 189 | Up, 190 | Down, 191 | Left, 192 | Right 193 | } 194 | 195 | let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right]; 196 | ``` 197 | 198 | 之前提到過,`declare` 定義的型別只會用於編譯時的檢查,編譯結果中會被刪除。 199 | 200 | 上例的編譯結果是: 201 | 202 | ```javascript 203 | var directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right]; 204 | ``` 205 | 206 | 外部列舉與宣告語句一樣,常出現在宣告檔案中。 207 | 208 | 同時使用 `declare` 和 `const` 也是可以的: 209 | 210 | ```typescript 211 | declare const enum Directions { 212 | Up, 213 | Down, 214 | Left, 215 | Right 216 | } 217 | 218 | let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right]; 219 | ``` 220 | 221 | 編譯結果: 222 | 223 | ```javascript 224 | var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */]; 225 | ``` 226 | 227 | > TypeScript 的列舉型別的概念[來源於 C\#](https://msdn.microsoft.com/zh-cn/library/sbbt4032.aspx)。 228 | 229 | ## 參考 230 | 231 | * [Enums](http://www.typescriptlang.org/docs/handbook/enums.html)([中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Enums.html)) 232 | * [C\# Enum](https://msdn.microsoft.com/zh-cn/library/sbbt4032.aspx) 233 | * [上一章:元組](tuple.md) 234 | * [下一章:類別](class.md) 235 | 236 | -------------------------------------------------------------------------------- /advanced/further-reading.md: -------------------------------------------------------------------------------- 1 | # 延伸閱讀 2 | 3 | 此處記錄了[官方手冊](http://www.typescriptlang.org/docs/handbook/basic-types.html)([中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/))中包含,但是本書未涉及的概念。 4 | 5 | 我認為它們是一些不重要或者不屬於 TypeScript 的概念,所以這裡只給出一個簡單的釋義,詳細內容可以點選連結深入理解。 6 | 7 | * [Never](http://www.typescriptlang.org/docs/handbook/basic-types.html#never)([中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Basic%20Types.html#never)):永遠不存在值的型別,一般用於錯誤處理函式 8 | * [Variable Declarations](http://www.typescriptlang.org/docs/handbook/variable-declarations.html)([中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Variable%20Declarations.html)):使用 `let` 和 `const` 替代 `var`,這是 [ES6 的知識](http://es6.ruanyifeng.com/#docs/let) 9 | * [`this`](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Functions.html#this):箭頭函式的運用,這是 [ES6 的知識](http://es6.ruanyifeng.com/#docs/function) 10 | * [Using Class Types in Generics](http://www.typescriptlang.org/docs/handbook/generics.html#using-class-types-in-generics)([中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Generics.html#在泛型裡使用類別型別)):建立工廠函式時,需要參考建構函式的類別型別 11 | * [Best common type](http://www.typescriptlang.org/docs/handbook/type-inference.html#best-common-type)([中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Type%20Inference.html#最佳通用型別)):陣列的型別推論 12 | * [Contextual Type](http://www.typescriptlang.org/docs/handbook/type-inference.html#contextual-type)([中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Type%20Inference.html#上下文型別)):函式輸入的型別推論 13 | * [Type Compatibility](http://www.typescriptlang.org/docs/handbook/type-compatibility.html)([中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Type%20Compatibility.html)):允許不嚴格符合型別,只需要在一定規則下相容即可 14 | * [Advanced Types](http://www.typescriptlang.org/docs/handbook/advanced-types.html#intersection-types)([中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Advanced%20Types.html#交叉型別(intersection-types))):使用 `&` 將多種型別的共有部分疊加成一種型別 15 | * [Type Guards and Differentiating Types](http://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-differentiating-types)([中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Advanced%20Types.html#型別保護與區分類型(type-guards-and-differentiating-types))):聯合型別在一些情況下被識別為特定的型別 16 | * [Discriminated Unions](http://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions)([中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Advanced%20Types.html#可辨識聯合(discriminated-unions))):使用 `|` 聯合多個介面的時候,透過一個共有的屬性形成可辨識聯合 17 | * [Polymorphic `this` types](http://www.typescriptlang.org/docs/handbook/advanced-types.html#polymorphic-this-types)([中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Advanced%20Types.html#多型的this型別)):父類別的某個方法返回 `this`,當子類別繼承父類別後,子類別的實例呼叫此方法,返回的 `this` 能夠被 TypeScript 正確的識別為子類別的實例。 18 | * [Symbols](http://www.typescriptlang.org/docs/handbook/symbols.html)([中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Symbols.html)):新原生型別,這是 [ES6 的知識](http://es6.ruanyifeng.com/#docs/symbol) 19 | * [Iterators and Generators](http://www.typescriptlang.org/docs/handbook/iterators-and-generators.html)([中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Iterators%20and%20Generators.html)):迭代器,這是 [ES6 的知識](http://es6.ruanyifeng.com/#docs/iterator) 20 | * [Namespaces](http://www.typescriptlang.org/docs/handbook/namespaces.html)([中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Namespaces.html)):避免全域性汙染,現在已被 [ES6 Module](http://es6.ruanyifeng.com/#docs/module) 替代 21 | * [Decorators](http://www.typescriptlang.org/docs/handbook/decorators.html)([中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Decorators.html)):修飾器,這是 [ES7 的一個提案](http://es6.ruanyifeng.com/#docs/decorator) 22 | * [Mixins](http://www.typescriptlang.org/docs/handbook/mixins.html)([中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Mixins.html)):一種程式設計模式,與 TypeScript 沒有直接關係,可以參考 [ES6 中 Mixin 模式的實現](http://es6.ruanyifeng.com/#docs/class#Mixin模式的實現) 23 | * [上一章:宣告合併](declaration-merging.md) 24 | * [下一章:工程](../engineering/) 25 | 26 | -------------------------------------------------------------------------------- /advanced/generics.md: -------------------------------------------------------------------------------- 1 | # 泛型 2 | 3 | 泛型(Generics)是指在定義函式、介面或類別的時候,不預先指定具體的型別,而在使用的時候再指定型別的一種特性。 4 | 5 | ## 簡單的例子 6 | 7 | 首先,我們來實現一個函式 `createArray`,它可以建立一個指定長度的陣列,同時將每一項都填充一個預設值: 8 | 9 | ```typescript 10 | function createArray(length: number, value: any): Array { 11 | let result = []; 12 | for (let i = 0; i < length; i++) { 13 | result[i] = value; 14 | } 15 | return result; 16 | } 17 | 18 | createArray(3, 'x'); // ['x', 'x', 'x'] 19 | ``` 20 | 21 | 上例中,我們使用了[之前提到過的陣列泛型](../basics/type-of-array.md#陣列泛型)來定義返回值的型別。 22 | 23 | 這段程式碼編譯不會報錯,但是一個顯而易見的缺陷是,它並沒有準確的定義返回值的型別: 24 | 25 | `Array` 允許陣列的每一項都為任意型別。但是我們預期的是,陣列中每一項都應該是輸入的 `value` 的型別。 26 | 27 | 這時候,泛型就派上用場了: 28 | 29 | ```typescript 30 | function createArray(length: number, value: T): Array { 31 | let result: T[] = []; 32 | for (let i = 0; i < length; i++) { 33 | result[i] = value; 34 | } 35 | return result; 36 | } 37 | 38 | createArray(3, 'x'); // ['x', 'x', 'x'] 39 | ``` 40 | 41 | 上例中,我們在函式名後添加了 ``,其中 `T` 用來指代任意輸入的型別,在後面的輸入 `value: T` 和輸出 `Array` 中即可使用了。 42 | 43 | 接著在呼叫的時候,可以指定它具體的型別為 `string`。當然,也可以不手動指定,而讓型別推論自動推算出來: 44 | 45 | ```typescript 46 | function createArray(length: number, value: T): Array { 47 | let result: T[] = []; 48 | for (let i = 0; i < length; i++) { 49 | result[i] = value; 50 | } 51 | return result; 52 | } 53 | 54 | createArray(3, 'x'); // ['x', 'x', 'x'] 55 | ``` 56 | 57 | ## 多個型別引數 58 | 59 | 定義泛型的時候,可以一次定義多個型別引數: 60 | 61 | ```typescript 62 | function swap(tuple: [T, U]): [U, T] { 63 | return [tuple[1], tuple[0]]; 64 | } 65 | 66 | swap([7, 'seven']); // ['seven', 7] 67 | ``` 68 | 69 | 上例中,我們定義了一個 `swap` 函式,用來交換輸入的元組。 70 | 71 | ## 泛型約束 72 | 73 | 在函式內部使用泛型變數的時候,由於事先不知道它是哪種型別,所以不能隨意的操作它的屬性或方法: 74 | 75 | ```typescript 76 | function loggingIdentity(arg: T): T { 77 | console.log(arg.length); 78 | return arg; 79 | } 80 | 81 | // index.ts(2,19): error TS2339: Property 'length' does not exist on type 'T'. 82 | ``` 83 | 84 | 上例中,泛型 `T` 不一定包含屬性 `length`,所以編譯的時候報錯了。 85 | 86 | 這時,我們可以對泛型進行約束,只允許這個函式傳入那些包含 `length` 屬性的變數。這就是泛型約束: 87 | 88 | ```typescript 89 | interface Lengthwise { 90 | length: number; 91 | } 92 | 93 | function loggingIdentity(arg: T): T { 94 | console.log(arg.length); 95 | return arg; 96 | } 97 | ``` 98 | 99 | 上例中,我們使用了 `extends` 約束了泛型 `T` 必須符合介面 `Lengthwise` 的形狀,也就是必須包含 `length` 屬性。 100 | 101 | 此時如果呼叫 `loggingIdentity` 的時候,傳入的 `arg` 不包含 `length`,那麼在編譯階段就會報錯了: 102 | 103 | ```typescript 104 | interface Lengthwise { 105 | length: number; 106 | } 107 | 108 | function loggingIdentity(arg: T): T { 109 | console.log(arg.length); 110 | return arg; 111 | } 112 | 113 | loggingIdentity(7); 114 | 115 | // index.ts(10,17): error TS2345: Argument of type '7' is not assignable to parameter of type 'Lengthwise'. 116 | ``` 117 | 118 | 多個型別引數之間也可以互相約束: 119 | 120 | ```typescript 121 | function copyFields(target: T, source: U): T { 122 | for (let id in source) { 123 | target[id] = (source)[id]; 124 | } 125 | return target; 126 | } 127 | 128 | let x = { a: 1, b: 2, c: 3, d: 4 }; 129 | 130 | copyFields(x, { b: 10, d: 20 }); 131 | ``` 132 | 133 | 上例中,我們使用了兩個型別引數,其中要求 `T` 繼承 `U`,這樣就保證了 `U` 上不會出現 `T` 中不存在的欄位。 134 | 135 | ## 泛型介面 136 | 137 | [之前學習過](../basics/type-of-function.md#介面中函式的定義),可以使用介面的方式來定義一個函式需要符合的形狀: 138 | 139 | ```typescript 140 | interface SearchFunc { 141 | (source: string, subString: string): boolean; 142 | } 143 | 144 | let mySearch: SearchFunc; 145 | mySearch = function(source: string, subString: string) { 146 | return source.search(subString) !== -1; 147 | } 148 | ``` 149 | 150 | 當然也可以使用含有泛型的介面來定義函式的形狀: 151 | 152 | ```typescript 153 | interface CreateArrayFunc { 154 | (length: number, value: T): Array; 155 | } 156 | 157 | let createArray: CreateArrayFunc; 158 | createArray = function(length: number, value: T): Array { 159 | let result: T[] = []; 160 | for (let i = 0; i < length; i++) { 161 | result[i] = value; 162 | } 163 | return result; 164 | } 165 | 166 | createArray(3, 'x'); // ['x', 'x', 'x'] 167 | ``` 168 | 169 | 進一步,我們可以把泛型引數提前到介面名上: 170 | 171 | ```typescript 172 | interface CreateArrayFunc { 173 | (length: number, value: T): Array; 174 | } 175 | 176 | let createArray: CreateArrayFunc; 177 | createArray = function(length: number, value: T): Array { 178 | let result: T[] = []; 179 | for (let i = 0; i < length; i++) { 180 | result[i] = value; 181 | } 182 | return result; 183 | } 184 | 185 | createArray(3, 'x'); // ['x', 'x', 'x'] 186 | ``` 187 | 188 | 注意,此時在使用泛型介面的時候,需要定義泛型的型別。 189 | 190 | ## 泛型類別 191 | 192 | 與泛型介面類似,泛型也可以用於類別的型別定義中: 193 | 194 | ```typescript 195 | class GenericNumber { 196 | zeroValue: T; 197 | add: (x: T, y: T) => T; 198 | } 199 | 200 | let myGenericNumber = new GenericNumber(); 201 | myGenericNumber.zeroValue = 0; 202 | myGenericNumber.add = function(x, y) { return x + y; }; 203 | ``` 204 | 205 | ## 泛型引數的預設型別 206 | 207 | 在 TypeScript 2.3 以後,我們可以為泛型中的型別引數指定預設型別。當使用泛型時沒有在程式碼中直接指定型別引數,從實際值引數中也無法推測出時,這個預設型別就會起作用。 208 | 209 | ```typescript 210 | function createArray(length: number, value: T): Array { 211 | let result: T[] = []; 212 | for (let i = 0; i < length; i++) { 213 | result[i] = value; 214 | } 215 | return result; 216 | } 217 | ``` 218 | 219 | ## 參考 220 | 221 | * [Generics](http://www.typescriptlang.org/docs/handbook/generics.html)([中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/generics.html)) 222 | * [Generic parameter defaults](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-3.html#generic-parameter-defaults) 223 | * [上一章:類別與介面](class-and-interfaces.md) 224 | * [下一章:宣告合併](declaration-merging.md) 225 | 226 | -------------------------------------------------------------------------------- /advanced/string-literal-types.md: -------------------------------------------------------------------------------- 1 | # 字串字面量型別 2 | 3 | 字串字面量型別用來約束取值只能是某幾個字串中的一個。 4 | 5 | ## 簡單的例子 6 | 7 | ```typescript 8 | type EventNames = 'click' | 'scroll' | 'mousemove'; 9 | function handleEvent(ele: Element, event: EventNames) { 10 | // do something 11 | } 12 | 13 | handleEvent(document.getElementById('hello'), 'scroll'); // 沒問題 14 | handleEvent(document.getElementById('world'), 'dbclick'); // 報錯,event 不能為 'dbclick' 15 | 16 | // index.ts(7,47): error TS2345: Argument of type '"dbclick"' is not assignable to parameter of type 'EventNames'. 17 | ``` 18 | 19 | 上例中,我們使用 `type` 定了一個字串字面量型別 `EventNames`,它只能取三種字串中的一種。 20 | 21 | 注意,**型別別名與字串字面量型別都是使用 `type` 進行定義。** 22 | 23 | ## 參考 24 | 25 | * [Advanced Types \# Type Aliases](http://www.typescriptlang.org/docs/handbook/advanced-types.html#string-literal-types)([中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Advanced%20Types.html#字串字面量型別)) 26 | * [上一章:型別別名](type-aliases.md) 27 | * [下一章:元組](tuple.md) 28 | 29 | -------------------------------------------------------------------------------- /advanced/tuple.md: -------------------------------------------------------------------------------- 1 | # 元組 2 | 3 | 數組合並了相同型別的物件,而元組(Tuple)合併了不同型別的物件。 4 | 5 | 元組起源於函式程式語言(如 F\#),這些語言中會頻繁使用元組。 6 | 7 | ## 簡單的例子 8 | 9 | 定義一對值分別為 `string` 和 `number` 的元組: 10 | 11 | ```typescript 12 | let tom: [string, number] = ['Tom', 25]; 13 | ``` 14 | 15 | 當賦值或訪問一個已知索引的元素時,會得到正確的型別: 16 | 17 | ```typescript 18 | let tom: [string, number]; 19 | tom[0] = 'Tom'; 20 | tom[1] = 25; 21 | 22 | tom[0].slice(1); 23 | tom[1].toFixed(2); 24 | ``` 25 | 26 | 也可以只賦值其中一項: 27 | 28 | ```typescript 29 | let tom: [string, number]; 30 | tom[0] = 'Tom'; 31 | ``` 32 | 33 | 但是當直接對元組型別的變數進行初始化或者賦值的時候,需要提供所有元組型別中指定的項。 34 | 35 | ```typescript 36 | let tom: [string, number]; 37 | tom = ['Tom', 25]; 38 | ``` 39 | 40 | ```typescript 41 | let tom: [string, number]; 42 | tom = ['Tom']; 43 | 44 | // Property '1' is missing in type '[string]' but required in type '[string, number]'. 45 | ``` 46 | 47 | ## 越界的元素 48 | 49 | 當新增越界的元素時,它的型別會被限制為元組中每個型別的聯合型別: 50 | 51 | ```typescript 52 | let tom: [string, number]; 53 | tom = ['Tom', 25]; 54 | tom.push('male'); 55 | tom.push(true); 56 | 57 | // Argument of type 'true' is not assignable to parameter of type 'string | number'. 58 | ``` 59 | 60 | ## 參考 61 | 62 | * [Basic Types \# Tuple](http://www.typescriptlang.org/docs/handbook/basic-types.html#tuple)([中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Basic%20Types.html#元組-tuple)) 63 | * [上一章:字串字面量型別](string-literal-types.md) 64 | * [下一章:列舉](enum.md) 65 | 66 | -------------------------------------------------------------------------------- /advanced/type-aliases.md: -------------------------------------------------------------------------------- 1 | # 型別別名 2 | 3 | 型別別名用來給一個型別起個新名字。 4 | 5 | ## 簡單的例子 6 | 7 | ```typescript 8 | type Name = string; 9 | type NameResolver = () => string; 10 | type NameOrResolver = Name | NameResolver; 11 | function getName(n: NameOrResolver): Name { 12 | if (typeof n === 'string') { 13 | return n; 14 | } else { 15 | return n(); 16 | } 17 | } 18 | ``` 19 | 20 | 上例中,我們使用 `type` 建立型別別名。 21 | 22 | 型別別名常用於聯合型別。 23 | 24 | ## 參考 25 | 26 | * [Advanced Types \# Type Aliases](http://www.typescriptlang.org/docs/handbook/advanced-types.html#type-aliases)([中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Advanced%20Types.html#型別別名)) 27 | * [上一章:進階](./) 28 | * [下一章:字串字面量型別](string-literal-types.md) 29 | 30 | -------------------------------------------------------------------------------- /assets/graphs.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doggy8088/typescript-tutorial/87baecb9eab329de934b1d14bd3245f546503633/assets/graphs.pptx -------------------------------------------------------------------------------- /assets/typescript-eslint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doggy8088/typescript-tutorial/87baecb9eab329de934b1d14bd3245f546503633/assets/typescript-eslint.png -------------------------------------------------------------------------------- /assets/wechat.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doggy8088/typescript-tutorial/87baecb9eab329de934b1d14bd3245f546503633/assets/wechat.jpeg -------------------------------------------------------------------------------- /basics/README.md: -------------------------------------------------------------------------------- 1 | # 基礎 2 | 3 | 本部分介紹了 TypeScript 中的常用型別和一些基本概念,旨在讓大家對 TypeScript 有個初步的理解。具體內容包括: 4 | 5 | * [原始資料型別](primitive-data-types.md) 6 | * [任意值](any.md) 7 | * [型別推論](type-inference.md) 8 | * [聯合型別](union-types.md) 9 | * [物件的型別——介面](type-of-object-interfaces.md) 10 | * [陣列的型別](type-of-array.md) 11 | * [函式的型別](type-of-function.md) 12 | * [型別斷言](type-assertion.md) 13 | * [宣告檔案](declaration-files.md) 14 | * [內建物件](built-in-objects.md) 15 | * [上一章:Hello TypeScript](../introduction/hello-typescript.md) 16 | * [下一章:原始資料型別](primitive-data-types.md) 17 | 18 | -------------------------------------------------------------------------------- /basics/any.md: -------------------------------------------------------------------------------- 1 | # 任意值 2 | 3 | 任意值(Any)用來表示允許賦值為任意型別。 4 | 5 | ## 什麼是任意值型別 6 | 7 | 如果是一個普通型別,在賦值過程中改變型別是不被允許的: 8 | 9 | ```typescript 10 | let myFavoriteNumber: string = 'seven'; 11 | myFavoriteNumber = 7; 12 | 13 | // index.ts(2,1): error TS2322: Type 'number' is not assignable to type 'string'. 14 | ``` 15 | 16 | 但如果是 `any` 型別,則允許被賦值為任意型別。 17 | 18 | ```typescript 19 | let myFavoriteNumber: any = 'seven'; 20 | myFavoriteNumber = 7; 21 | ``` 22 | 23 | ## 任意值的屬性和方法 24 | 25 | 在任意值上訪問任何屬性都是允許的: 26 | 27 | ```typescript 28 | let anyThing: any = 'hello'; 29 | console.log(anyThing.myName); 30 | console.log(anyThing.myName.firstName); 31 | ``` 32 | 33 | 也允許呼叫任何方法: 34 | 35 | ```typescript 36 | let anyThing: any = 'Tom'; 37 | anyThing.setName('Jerry'); 38 | anyThing.setName('Jerry').sayHello(); 39 | anyThing.myName.setFirstName('Cat'); 40 | ``` 41 | 42 | 可以認為,**宣告一個變數為任意值之後,對它的任何操作,返回的內容的型別都是任意值**。 43 | 44 | ## 未宣告型別的變數 45 | 46 | **變數如果在宣告的時候,未指定其型別,那麼它會被識別為任意值型別**: 47 | 48 | ```typescript 49 | let something; 50 | something = 'seven'; 51 | something = 7; 52 | 53 | something.setName('Tom'); 54 | ``` 55 | 56 | 等價於 57 | 58 | ```typescript 59 | let something: any; 60 | something = 'seven'; 61 | something = 7; 62 | 63 | something.setName('Tom'); 64 | ``` 65 | 66 | ## 參考 67 | 68 | * [Basic Types \# Any](http://www.typescriptlang.org/docs/handbook/basic-types.html#any)([中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Basic%20Types.html#任意值)) 69 | * [上一章:原始資料型別](primitive-data-types.md) 70 | * [下一章:型別推論](type-inference.md) 71 | 72 | -------------------------------------------------------------------------------- /basics/built-in-objects.md: -------------------------------------------------------------------------------- 1 | # 內建物件 2 | 3 | JavaScript 中有很多[內建物件](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects),它們可以直接在 TypeScript 中當做定義好了的型別。 4 | 5 | 內建物件是指根據標準在全域性作用域(Global)上存在的物件。這裡的標準是指 ECMAScript 和其他環境(比如 DOM)的標準。 6 | 7 | ## ECMAScript 的內建物件 8 | 9 | ECMAScript 標準提供的內建物件有: 10 | 11 | `Boolean`、`Error`、`Date`、`RegExp` 等。 12 | 13 | 我們可以在 TypeScript 中將變數定義為這些型別: 14 | 15 | ```typescript 16 | let b: Boolean = new Boolean(1); 17 | let e: Error = new Error('Error occurred'); 18 | let d: Date = new Date(); 19 | let r: RegExp = /[a-z]/; 20 | ``` 21 | 22 | 更多的內建物件,可以檢視 [MDN 的文件](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects)。 23 | 24 | 而他們的定義檔案,則在 [TypeScript 核心函式庫的定義檔案](https://github.com/Microsoft/TypeScript/tree/master/src/lib)中。 25 | 26 | ## DOM 和 BOM 的內建物件 27 | 28 | DOM 和 BOM 提供的內建物件有: 29 | 30 | `Document`、`HTMLElement`、`Event`、`NodeList` 等。 31 | 32 | TypeScript 中會經常用到這些型別: 33 | 34 | ```typescript 35 | let body: HTMLElement = document.body; 36 | let allDiv: NodeList = document.querySelectorAll('div'); 37 | document.addEventListener('click', function(e: MouseEvent) { 38 | // Do something 39 | }); 40 | ``` 41 | 42 | 它們的定義檔案同樣在 [TypeScript 核心函式庫的定義檔案](https://github.com/Microsoft/TypeScript/tree/master/src/lib)中。 43 | 44 | ## TypeScript 核心函式庫的定義檔案 45 | 46 | [TypeScript 核心函式庫的定義檔案](https://github.com/Microsoft/TypeScript/tree/master/src/lib)中定義了所有瀏覽器環境需要用到的型別,並且是預置在 TypeScript 中的。 47 | 48 | 當你在使用一些常用的方法的時候,TypeScript 實際上已經幫你做了很多型別判斷的工作了,比如: 49 | 50 | ```typescript 51 | Math.pow(10, '2'); 52 | 53 | // index.ts(1,14): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. 54 | ``` 55 | 56 | 上面的例子中,`Math.pow` 必須接受兩個 `number` 型別的引數。事實上 `Math.pow` 的型別定義如下: 57 | 58 | ```typescript 59 | interface Math { 60 | /** 61 | * Returns the value of a base expression taken to a specified power. 62 | * @param x The base value of the expression. 63 | * @param y The exponent value of the expression. 64 | */ 65 | pow(x: number, y: number): number; 66 | } 67 | ``` 68 | 69 | 再舉一個 DOM 中的例子: 70 | 71 | ```typescript 72 | document.addEventListener('click', function(e) { 73 | console.log(e.targetCurrent); 74 | }); 75 | 76 | // index.ts(2,17): error TS2339: Property 'targetCurrent' does not exist on type 'MouseEvent'. 77 | ``` 78 | 79 | 上面的例子中,`addEventListener` 方法是在 TypeScript 核心函式庫中定義的: 80 | 81 | ```typescript 82 | interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEvent { 83 | addEventListener(type: string, listener: (ev: MouseEvent) => any, useCapture?: boolean): void; 84 | } 85 | ``` 86 | 87 | 所以 `e` 被推斷成了 `MouseEvent`,而 `MouseEvent` 是沒有 `targetCurrent` 屬性的,所以報錯了。 88 | 89 | 注意,TypeScript 核心函式庫的定義中不包含 Node.js 部分。 90 | 91 | ## 用 TypeScript 寫 Node.js 92 | 93 | Node.js 不是內建物件的一部分,如果想用 TypeScript 寫 Node.js,則需要引入第三方宣告檔案: 94 | 95 | ```bash 96 | npm install @types/node --save-dev 97 | ``` 98 | 99 | ## 參考 100 | 101 | * [內建物件](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects) 102 | * [TypeScript 核心函式庫的定義檔案](https://github.com/Microsoft/TypeScript/tree/master/src/lib) 103 | * [上一章:宣告檔案](declaration-files.md) 104 | * [下一章:進階](../advanced/) 105 | 106 | -------------------------------------------------------------------------------- /basics/declaration-files.md: -------------------------------------------------------------------------------- 1 | # 宣告檔案 2 | 3 | 當使用第三方函式庫時,我們需要參考它的宣告檔案,才能獲得對應的程式碼自動完成、介面提示等功能。 4 | 5 | ## 新語法索引 6 | 7 | 由於本章涉及大量新語法,故在本章開頭列出新語法的索引,方便大家在使用這些新語法時能快速查詢到對應的講解: 8 | 9 | * [`declare var`](declaration-files.md#declare-var) 宣告全域性變數 10 | * [`declare function`](declaration-files.md#declare-function) 宣告全域性方法 11 | * [`declare class`](declaration-files.md#declare-class) 宣告全域性類別 12 | * [`declare enum`](declaration-files.md#declare-enum) 宣告全域性列舉型別 13 | * [`declare namespace`](declaration-files.md#declare-namespace) 宣告(含有子屬性的)全域性物件 14 | * [`interface` 和 `type`](declaration-files.md#interface-he-type) 宣告全域性型別 15 | * [`export`](declaration-files.md#export) 匯出變數 16 | * [`export namespace`](declaration-files.md#export-namespace) 匯出(含有子屬性的)物件 17 | * [`export default`](declaration-files.md#export-default) ES6 預設匯出 18 | * [`export =`](declaration-files.md#export-1) commonjs 匯出模組 19 | * [`export as namespace`](declaration-files.md#export-as-namespace) UMD 函式庫宣告全域性變數 20 | * [`declare global`](declaration-files.md#declare-global) 擴充套件全域性變數 21 | * [`declare module`](declaration-files.md#declare-module) 擴充套件模組 22 | * [`/// `](declaration-files.md#san-xie-xian-zhi-ling) 三斜線指令 23 | 24 | ## 什麼是宣告語句 25 | 26 | 假如我們想使用第三方函式庫 jQuery,一種常見的方式是在 html 中透過 `