這篇文章是我在公司前端小組內部的演講分享稿,目的是教會你們使用 TypeScript,
這篇文章雖然標着基礎,但我指的基礎是學完後就可以勝任 TypeScript 的開發工做。
從我分享完的效果來看,你們都學會了,效果仍是好的。我把這篇分享稿發佈出來,
但願能對你們的 TypeScript 學習有所幫助。
TypeScript 是由微軟發佈的一款開源的編程語言。它是 JavaScript 的超集(兼容 JavaScript 代碼),其代碼必須通過編譯後,方可在 JavaScript 環境中運行,核心功能是類型系統和能夠提早使用 ES 的新特性。javascript
接下來來看一段代碼示例:html
// TypeScript 語法 function add(x: number, y: number): number { const res: number = x + y; return res; } // 與 C 語言比較 int add(int x, int y) { int res = x + y; return res; }
當類型不對的時候,IDE 會提示錯誤:
前端
編譯後:vue
// JavaScript 語法 function add(x, y) { const res = x + y; return res; }
聯想:大體能夠把它當作是加了類型系統的 Babel。java
$ yarn global add typescript // 測試是否安裝成功 $ tsc -v Version 3.4.5
// add.ts function add(x: number, y: number): number { const res: number = x + y; return res; } export default add;
$ tsc add.ts
// add.js function add(x, y) { const res = x + y; return res; } export default add;
適用於邊開發邊看結果的狀況:react
$ tsc -w add.ts Starting compilation in watch mode... Watching for file changes.
// 布爾值 let isDone: boolean = false; // 數值 let age: number = 12; // 字符串 let name: string = 'Jay'; // 數組 let list: number[] = [1, 2, 3]; let list: Array<number> = [1, 2, 3]; // 對象 let mokey: object = {name: 'wu kong'}; // 空值,表示沒有返回值 function print(): void {} // 任意值 let goods: any = {}; let goods: any = 2019; // 未指定類型,視爲任意值 let goods; // 類型推斷 let name = 'Jay'; name = 123; // 報錯 // 聯合類型 let name: string | number; name = 'Jay'; name = 123;
類是對屬性和方法的封裝webpack
類的示例:git
// 跟 JavaScript 的類類似,多了訪問控制等關鍵詞 class Monkey { public height: number; private age: number = 12; public static mkName: string = 'kinkong'; private static action: string = 'jump'; constructor(height: number) { this.height = height; } public getAge(): number { return this.age; } } const monkey = new Monkey(120); monkey.getAge(); const height = monkey.height; const age = monkey.age; // 報錯 const mkName = Monkey.mkName; const action = Monkey.action; // 報錯
class Animal { move(distanceInMeters: number = 0) { console.log(`Animal moved ${distanceInMeters}m.`); } } class Dog extends Animal { bark() { console.log('Woof! Woof!'); } } const dog = new Dog(); dog.bark(); dog.move(10);
class Person { protected name: string; private age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } } class Employee extends Person { private readonly initial: string = 'abc'; private readonly department: string; constructor(name: string, department: string) { super(name, 1); this.department = department; } public getElevatorPitch() { return `Hello, my name is ${this.name} and I work in ${this.department}.`; } public getAge() { return this.age; } } let howard = new Employee("Howard", "Sales"); console.log(howard.getElevatorPitch()); console.log(howard.name); // error
let passcode = "secret passcode"; class Employee { private _fullName: string; get fullName(): string { return this._fullName; } set fullName(newName: string) { if (passcode && passcode == "secret passcode") { this._fullName = newName; } else { console.log("Error: Unauthorized update of employee!"); } } } let employee = new Employee(); employee.fullName = "Bob Smith"; if (employee.fullName) { console.log(employee.fullName); }
class Point { x: number; y: number; } interface Point3d extends Point { z: number; } let point3d: Point3d = {x: 1, y: 2, z: 3};
接口是用來定義規範的,經常用做類型說明。
接口的示例:github
// 定義規範,限定類型 interface Point { x: number; y: number; // 可選屬性 z?: number; } let pt: Ponit = {x: 1, y: 1, z: 1}; function setPoint(point: Point): void {} setPoint({x: 1, y: 1}); // 實現接口 class point implements Point { x: number; y: number; constructor(x: number, y: number) { this.x = x; this.y = y; } public getPoint(): object { return { x: this.x, y: this.y, } } } const p = new point(3, 4); p.getPoint();
interface Shape { color: string; } interface Square extends Shape { sideLength: number; }
interface ClockConstructor { new (hour: number, minute: number); } class Clock implements ClockConstructor { currentTime: Date; constructor(h: number, m: number) { } }
當 TypeScript 提供的類型不夠用時,能夠用來自定義類型,供本身使用。web
// type 定義新類型,至關於類型別名 type myType = string | number | boolean // 使用: const foo: myType = 'foo' // type 定義函數類型 type hello = (msg: string) => void // type 對象類型 type WebSite = { url: string; title: number; }
命名空間主要有兩個方面的用途:
namespace Validator { export interface StringValidator { isAcceptable(s: string): boolean; } const lettersRegexp = /^[A-Za-z]+$/; const numberRegexp = /^[0-9]+$/; // 當聲明一個命名空間的時候,全部實體部分默認是私有的,可使用 export 關鍵字導出公共部分。 export class LettersOnlyValidator implements StringValidator { isAcceptable(s: string) { return lettersRegexp.test(s); } } export class ZipCodeValidator implements StringValidator { isAcceptable(s: string) { return s.length === 5 && numberRegexp.test(s); } } } new Validator.LettersOnlyValidator(); interface MyClassMethodOptions { name?: string; age?: number; } namespace MyClass { export interface MyClassMethodOptions { width?: number; height?: number; } }
/// <reference path="validate.ts" /> const v = new Validator.LettersOnlyValidator();
泛型容許在強類型程序設計語言中編寫代碼時,使用一些之後才指定的類型,在實際調用時纔去指定泛型的類型。
舉例:
// 模擬服務,提供不一樣的數據。這裏模擬了一個字符串和一個數值 const service = { getStringValue: function() { return "a string value"; }, getNumberValue: function() { return 20; } }; // 處理數據的中間件 function middleware(value: string): string { return value; } // 沒問題 middleware(service.getStringValue()); // 報錯:參數類型不對 middleware(service.getNumberValue()); // 設成 any 行不行? function middleware(value: any): any // 多寫幾個 middleware 行不行? function middleware1(value: string): string { ... } function middleware2(value: number): number { ... } // 改成泛型 function middleware<T>(value: T): T { return value; } // 使用 middleware<string>(service.getStringValue()); middleware<number>(service.getNumberValue());
xxx.d.ts
聲明文件若是把 add.js 發佈成一個 npm 包,別人在使用的時候 IDE 的提示每每都不太友好:
那麼如何能讓 IDE 給 add 方法加上類型提示和參數提示呢?
TypeScript 提供了 xxx.d.ts
文件來給 IDE 使用。
xxx.d.ts
叫作聲明文件,通常用於類型提示和模塊補充,能夠自動生成,也能夠手動編寫,通常狀況下是不用咱們手動編寫的。
// 加上 -d 參數,就會生成 add.d.ts 文件 $ tsc -d add.ts
// add.d.ts declare function add(x: number, y: number): number; export default add;
declare 關鍵字
用於聲明你須要的變量、函數、類等,聲明之後 IDE 就能夠根據它來進行類型提示。
若是有 add.d.ts 文件,那麼它就承擔了類型提示的任務:
同時也至關於 add 方法的接口文檔:
有如下兩個場景會去手動建立這個文件:
// 定義模塊補充的語法 declare module 'filePath' { }
使用場景:vue-shim.d.ts 文件,爲了讓 TypeScript 識別 .vue 文件。
// 文件內容 declare module "*.vue" { import Vue from "vue"; export default Vue; }
指定輸出目錄:
$ tsc -d --strict -m ESNext --outDir lib index.ts
tsc 的全部參數能夠經過執行:tsc -h
來查看。
避免書寫冗長的命令,豐富的配置項。
生成 tsconfig.json 文件:
$ tsc --init
經常使用配置項解讀:
{ "compilerOptions": { "target": "ES5", /* target用於指定編譯以後的版本目標 version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ "module": "ESNext", /* 用來指定要使用的模塊標準: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ "declaration": true, /* 生成對應的 '.d.ts' 聲明文件 */ "outDir": "./lib", /* 指定編譯輸出目錄 */ "esModuleInterop": true, /* 經過爲導入內容建立命名空間,實現CommonJS和ES模塊之間的互操做性 */ "experimentalDecorators": true, /* 啓用 ES7 裝飾器語法 */ } }
配置後,執行如下命令,就會讀取配置文件:
$ tsc add.ts
上述的方法適用於開發命令行工具,好比 @xiyun/cli,開發 web 應用仍是不太方便。
若是想要方便地開發 web 應用,好比:開啓前端服務、模塊熱加載等,就須要配合構建工具使用。
parcel:極速零配置 Web 應用打包工具。
聯想:能夠當作是已經配置好了的 webpack。
全局安裝 parcel 工具:
$ yarn global add parcel-bundler
運行:
$ parcel index.html
它就會幫你生成 package.json,自動安裝 typeScript,啓動好開發服務,並支持熱加載。
最方便快捷:vue create my-app
,選擇 TypeScript。
若是要改造現有項目:
"devDependencies": { "@vue/cli-plugin-typescript": "^3.8.0", "typescript": "^3.4.3", }
main.js --> main.ts
<script lang="ts"> </script>
// 文件內容 declare module "*.vue" { import Vue from "vue"; export default Vue; }
若是想要用這種語法來寫 Vue 組件:
import { Component, Prop, Vue } from 'vue-property-decorator'; @Component export default class HelloWorld extends Vue { @Prop() private msg!: string; }
那麼須要加上這兩個依賴:
"dependencies": { // 官網出的ts支持包 "vue-class-component": "^7.0.2", // 對ts支持包更好封裝的裝飾器 "vue-property-decorator": "^8.1.0" },
建立應用時加上--typescript
參數便可:
$ create-react-app my-app --typescript