TypeScript 是 JavaScript 的類型的超集,它能夠編譯成純 JavaScript。編譯出來的 JavaScript 能夠運行在任何瀏覽器上。TypeScript 編譯工具能夠運行在任何服務器和任何系統上。javascript
JavaScript是一門弱類型/動態類型腳本語言。過於靈活,沒有類型檢查。在大型項目中代碼愈來愈複雜,JavaScript這種靈活的優點就變成了短板html
TypeScript 的命令行工具安裝方法以下:前端
npm install -g typescript
複製代碼
以上命令會在全局環境下安裝 tsc 命令,安裝完成以後,咱們就能夠在任何地方執行 tsc 命令了。java
注意:mac 的話 須要加上
sudo
node
sudo npm install -g typescript
複製代碼
Hello TypeScriptreact
首先建立一個01-ts.ts
.ts文件git
將如下代碼複製到 01-ts.ts 中:es6
const hello = name => {
console.log(`hello, ${name}`) } hello ('TS') 複製代碼
而後執行github
tsc 01-ts.ts
複製代碼
這時候會生成一個編譯好的文件 01-ts.ts:web
var hello = function (name) {
console.log("hello, " + name); }; hello('TS'); 複製代碼
TypeScript 中,使用 : 指定變量的類型,: 的先後有沒有空格均可以。
上述例子中,咱們用 : 指定name 參數類型爲 string。可是編譯爲 js 以後,並無什麼檢查的代碼被插入進來。
TypeScript 只會進行靜態檢查,若是發現有錯誤,編譯的時候就會報錯。
const hello = (name:string) => {
console.log(`hello, ${name}`) } hello (1) 複製代碼
編輯器中會提示錯誤,編譯的時候也會出錯:
01-ts.ts:5:8 - error TS2345: Argument of type '1' is not assignable to parameter of type 'string'.
5 hello (1) ~ 複製代碼
就算出現錯誤仍是會生成js文件:
var hello = function (name) {
console.log("hello, " + name); }; hello(1); 複製代碼
開始使用 tsconfig.json 是一件比較容易的事,你僅僅須要寫下:
{}
複製代碼
或者使用 tsc --init
建立tsconfig.json
在項目的根目錄下建立一個空 JSON 文件。經過這種方式,TypeScript 將 會把此目錄和子目錄下的全部 .ts 文件做爲編譯上下文的一部分,它還會包含一部分默認的編譯選項。
你能夠經過 compilerOptions 來定製你的編譯選項:
{
"compilerOptions": { /* 基本選項 */ "target": "es5", // 指定 ECMAScript 目標版本: 'ES3' (default), 'ES5', 'ES6'/'ES2015', 'ES2016', 'ES2017', or 'ESNEXT' "module": "commonjs", // 指定使用模塊: 'commonjs', 'amd', 'system', 'umd' or 'es2015' "lib": [], // 指定要包含在編譯中的庫文件 "allowJs": true, // 容許編譯 javascript 文件 "checkJs": true, // 報告 javascript 文件中的錯誤 "jsx": "preserve", // 指定 jsx 代碼的生成: 'preserve', 'react-native', or 'react' "declaration": true, // 生成相應的 '.d.ts' 文件 "sourceMap": true, // 生成相應的 '.map' 文件 "outFile": "./", // 將輸出文件合併爲一個文件 "outDir": "./", // 指定輸出目錄 "rootDir": "./", // 用來控制輸出目錄結構 --outDir. "removeComments": true, // 刪除編譯後的全部的註釋 "noEmit": true, // 不生成輸出文件 "importHelpers": true, // 從 tslib 導入輔助工具函數 "isolatedModules": true, // 將每一個文件作爲單獨的模塊 (與 'ts.transpileModule' 相似). /* 嚴格的類型檢查選項 */ "strict": true, // 啓用全部嚴格類型檢查選項 "noImplicitAny": true, // 在表達式和聲明上有隱含的 any類型時報錯 "strictNullChecks": true, // 啓用嚴格的 null 檢查 "noImplicitThis": true, // 當 this 表達式值爲 any 類型的時候,生成一個錯誤 "alwaysStrict": true, // 以嚴格模式檢查每一個模塊,並在每一個文件里加入 'use strict' /* 額外的檢查 */ "noUnusedLocals": true, // 有未使用的變量時,拋出錯誤 "noUnusedParameters": true, // 有未使用的參數時,拋出錯誤 "noImplicitReturns": true, // 並非全部函數裏的代碼都有返回值時,拋出錯誤 "noFallthroughCasesInSwitch": true, // 報告 switch 語句的 fallthrough 錯誤。(即,不容許 switch 的 case 語句貫穿) /* 模塊解析選項 */ "moduleResolution": "node", // 選擇模塊解析策略: 'node' (Node.js) or 'classic' (TypeScript pre-1.6) "baseUrl": "./", // 用於解析非相對模塊名稱的基目錄 "paths": {}, // 模塊名到基於 baseUrl 的路徑映射的列表 "rootDirs": [], // 根文件夾列表,其組合內容表示項目運行時的結構內容 "typeRoots": [], // 包含類型聲明的文件列表 "types": [], // 須要包含的類型聲明文件名列表 "allowSyntheticDefaultImports": true, // 容許從沒有設置默認導出的模塊中默認導入。 /* Source Map Options */ "sourceRoot": "./", // 指定調試器應該找到 TypeScript 文件而不是源文件的位置 "mapRoot": "./", // 指定調試器應該找到映射文件而不是生成文件的位置 "inlineSourceMap": true, // 生成單個 soucemaps 文件,而不是將 sourcemaps 生成不一樣的文件 "inlineSources": true, // 將代碼與 sourcemaps 生成到一個文件中,要求同時設置了 --inlineSourceMap 或 --sourceMap 屬性 /* 其餘選項 */ "experimentalDecorators": true, // 啓用裝飾器 "emitDecoratorMetadata": true // 爲裝飾器提供元數據的支持 } } 複製代碼
配置文件配好後 就能夠直接使用tsc
進行編譯
在默認狀況下,當你開始在一個新的 TypeScript 文件中寫下代碼時,它處於全局命名空間中。如在 foo.ts 裏的如下代碼。
const foo = 123;
複製代碼
若是你在相同的項目裏建立了一個新的文件 bar.ts,TypeScript 類型系統將會容許你使用變量 foo,就好像它在全局可用同樣:
const bar = foo; // allowed
複製代碼
毋庸置疑,使用全局變量空間是危險的,由於它會與文件內的代碼命名衝突。咱們推薦使用下文中將要提到的文件模塊
解決這個咱們只須要在文件最後 使用export {}
這樣每一個文件就是一個模塊,單獨的做用域
// 數字,2、8、十六進制都支持
let num:number =0 let num2:number=0xf00a // 字符串 let v:string="a" //let name:string="liu" // 這裏會提示 'name' was also declared here // 緣由是在默認狀態下,typescript 將 DOM typings 做爲全局的運行環境,因此當咱們聲明 name時, 與 DOM 中的全局 window 對象下的 name 屬性出現了重名。 // const a:string=null // 這裏是在tsconfig.json 配置了嚴格模式 "strict": true 改爲false 就行了 const c:boolean=false // void只能存放null/undefined 注意:在嚴格模式下只能是 undefined const d:void=undefined const f:null=null const g:undefined=undefined // 這裏報錯 'Symbol' only refers to a type, but is being used as a value here. Do you need to change your target library? Try changing the `lib` compiler option to es2015 or later. //是由於Symbol是es2015纔有的 ts使用的標準庫沒有 在tsconfig.json設置 "lib": ["es2015","DOM"] //之後遇到內置方法報錯的時候可能就是標準庫的關係 在在tsconfig.json設置 "lib" 就行了 const h:symbol=Symbol() 複製代碼
const foo:object = {}//能夠是 function (){} //[]//{}
const obj:{foo:number,bar:string}={foo:1,bar:'w'} // 這裏只是簡單的定義,跟專業的須要用接口來定義 export { } //確保跟其餘成員沒有衝突 複製代碼
// 寫法1
const arr1:Array<number>=[1,2,3] // 寫法2 const arr2:number[]=[1,2,3] let arr3:string[] = ["1","2"] let arr4:Array<string> = ["1","2"] // 小案例 // 若是是 JS,須要判斷是否是每一個成員都是數字 // 使用 TS,類型有保障,不用添加類型判斷 function sum (...args: number[]) { return args.reduce((prev, current) => prev + current, 0) } sum(1, 2, 3) // => 6 複製代碼
// 元組(Tuple)
export {} // 確保跟其它示例沒有成員衝突 const tuple: [number, string] = [18, 'zce'] // const age = tuple[0] // const name = tuple[1] const [age, name] = tuple // --------------------- const entries: [string, number][] = Object.entries({ foo: 123, bar: 456 }) const [key, value] = entries[0] // key => foo, value => 123 複製代碼
枚舉是對JavaScript標準數據類型集的擴充,常被用來限定在必定範圍內取值的場景,如一週只能有七天,顏色限定爲紅綠藍等。串的枚舉。咱們能夠用enum來實現。
// 枚舉
// 注意 enum 是採用 = 賦值 // enum PostStatus { // Draft = 0, // Unpublished = 1, // Published = 2 // } // 字符串枚舉 // enum PostStatus { // Draft = "a", // Unpublished = "b", // Published = "c" // } //---------------------------- // 能夠不用賦值 默認從0開始 // enum PostStatus { // Draft , //0 // Unpublished , //1 // Published //2 // } //---------------------------- // 給第一個賦值 後面的會在第一個基礎上自加 // enum PostStatus { // Draft =6, //6 // Unpublished , //7 // Published //8 // } 複製代碼
// 常量枚舉
const enum PostStatus { Draft , //0 Unpublished , //1 Published //2 } const post ={ status:PostStatus.Draft } // 編譯後 var post = { status: 1 /* Draft */ }; 複製代碼
我一般用 = 1 初始化,由於在枚舉類型值裏,它能讓你作一個安全可靠的檢查。
// 簡單案例
// function func1 (a: number, b: number): string { // return 'func1' // } // func1(100, 200) // 默認值或者能夠選參數 // b: number=1 // b?: number 問號標示可選 // function func1 (a: number, b: number=1): string { // return 'func1' // } // func1(100, 200) // 任意個數的參數 使用es6擴展運算符 // function func1 (...res:number): string { // return 'func1' // } // func1(100, 200) 複製代碼
// 任意類型(弱類型)
export {} // 確保跟其它示例沒有成員衝突 function stringify (value: any) { return JSON.stringify(value) } stringify('string') stringify(100) stringify(true) let foo: any = 'string' foo = 100 foo.bar() // any 類型是不安全的 複製代碼
若是沒有明確的指定類型,那麼 TypeScript 會依照類型推論(Type Inference)的規則推斷出一個類型。
let foo = 123; // foo 是 'number'
let bar = 'hello'; // bar 是 'string' foo = bar; // Error: 不能將 'string' 賦值給 `number` //數組 const bar = [1, 2, 3]; bar[0] = 'hello'; // Error:不能把 'string' 類型賦值給 'number' 類型 //對象 const foo = { a: 123, b: 456 }; foo.a = 'hello'; // Error:不能把 'string' 類型賦值給 'number' 類型 複製代碼
建議儘可能每一個變量都添加明確的類型,便於後期更直觀的理解代碼
若是類型不能被賦值推斷出來,類型也將不會流入函數參數中。例如以下的一個例子,編譯器並不知道 foo 的類型,所它也就不能推斷出 a 或者 b 的類型。
const foo = (a, b) => {
/* do something */ }; 複製代碼
然而,若是 foo 添加了類型註解,函數參數也就能被推斷(a,b 都能被推斷爲 number 類型):
type TwoNumberFunction = (a: number, b: number) => void;
const foo: TwoNumberFunction = (a, b) => { /* do something */ }; 複製代碼
儘管 TypeScript 通常狀況下能推斷函數的返回值,可是它可能並非你想要的。例如以下的 foo 函數,它的返回值爲 any:
function foo(a: number, b: number) {
return a + addOne(b); } // 一些使用 JavaScript 庫的特殊函數 function addOne(a) { return a + 1; } 複製代碼
這是由於返回值的類型被一個缺乏類型定義的 addOne 函數所影響(a 是 any,因此 addOne 返回值爲 any,foo 的返回值是也是 any)。
在某些狀況TS沒法檢測出類型,須要你告訴它。TypeScript 類型斷言用來告訴編譯器你比它更瞭解這個類型,而且它不該該再發出錯誤
// 類型斷言
export {} // 確保跟其它示例沒有成員衝突 // 假定這個 nums 來自一個明確的接口 const nums = [110, 120, 119, 112] const res = nums.find(i => i > 0) // res TypeScript 是不知道是什麼類型的 全部須要你告訴它 // const square = res * res // 推薦使用 as const num1 = res as number const num2 = <number>res // JSX 下不能使用 複製代碼
注意: 類型斷言不是類型轉換,是由於轉換一般意味着某種運行時的支持。可是,類型斷言純粹是一個編譯時語法,同時,它也是一種爲編譯器提供關於如何分析代碼的方法。
在面嚮對象語言中,接口(Interfaces)是一個很重要的概念,它是對行爲的抽象,而具體如何行動須要由類(classes)去實現(implement)。
// 接口
interface Post { title:string content:string } // 使用接口 function printPost (post: Post) { console.log(post.title) console.log(post.content) } printPost({title:'234',content:'23'}) 複製代碼
// ? 標示可選 // interface Post { // title:string // content:string, // subtitle?:string // } // // 使用接口 // function printPost (post: Post) { // console.log(post.title) // console.log(post.content) // } // readonly 只讀 interface Post { title:string content:string, subtitle?:string, readonly sun:string } // 使用接口 function printPost (post: Post) { console.log(post.title) console.log(post.content) } // 任意類型的屬性 interface Post { [prop:string]:string } // 使用接口 const cache:Post={ name:'sdffs', title:'s' } 複製代碼
export { }
class Person { // 首先先定義類型 name: string age: string constructor(name: string, age: string) { this.name = name this.age = age } sayHi(msg:string):void{ console.log(`${this.name}${msg}`); } } 複製代碼
class Person {
// 首先先定義類型 public name: string //公共 private age: string // 私有 protected gender: string // constructor(name: string, age: string) { this.name = name this.age = age this.gender = 'sss' } sayHi(msg: string): void { console.log(`${this.name}${msg}`); } } class Student extends Person { constructor(name: string, age: string) { super(name, age) console.log(this.gender) // 能夠訪問到 } } const tom = new Person('tom', '18') // console.log(tom.age); //訪問不到 // console.log(tom.gender); //也訪問不到 複製代碼
constructor 加修飾符
class Student extends Person {
private constructor (name: string, age: number) { super(name, age) console.log(this.gender) } static create (name: string, age: number) { return new Student(name, age) } } const tom = new Person('tom', 18) console.log(tom.name) // console.log(tom.age) // console.log(tom.gender) //不能直接經過 new來創造對象 const jack = Student.create('jack', 18) 複製代碼
使用`readonly`來定義只讀,若是有修飾符的話 跟在修飾符後面
class Person { // 首先先定義類型 public name: string //公共 private age: string // 私有 // 只讀成員 readonly protected readonly gender: boolean constructor(name: string, age: string) { this.name = name this.age = age this.gender = 'sss' } sayHi(msg: string): void { console.log(`${this.name}${msg}`); } } 複製代碼
實現(implements)是面向對象中的一個重要概念。通常來說,一個類只能繼承自另外一個類,有時候不一樣類之間能夠有一些共有的特性,這時候就能夠把特性提取成接口(interfaces),用 implements 關鍵字來實現。這個特性大大提升了面向對象的靈活性。
舉例來講,門是一個類,防盜門是門的子類。若是防盜門有一個報警器的功能,咱們能夠簡單的給防盜門添加一個報警方法。這時候若是有另外一個類,車,也有報警器的功能,就能夠考慮把報警器提取出來,做爲一個接口,防盜門和車都去實現它:
export{} // 先定義接口 interface Eat { // 定義方法類型 eat (food:string):void } interface Run { // 定義方法類型 run (distance:string):void } class Person implements Eat,Run{ eat(food:string):void{ console.log(food); } run (distance:string):void { console.log(distance); } } 複製代碼
使用abstract
來定義 抽象類
abstract class Animal {
eat (food: string): void { console.log(`呼嚕呼嚕的吃: ${food}`) } // 抽象方法不須要方法體 abstract run (distance: number): void } class Dog extends Animal { run(distance: number): void { console.log('四腳爬行', distance) } } const d = new Dog() d.eat('嗯西馬') d.run(100) 複製代碼
不能直接經過 new Animal() 建立實例
泛型(Generics)是指在定義函數、接口或類的時候,不預先指定具體的類型,而在使用的時候再指定類型的一種特性。
// 泛型
export {} // 確保跟其它示例沒有成員衝突 function createNumberArray (length: number, value: number): number[] { const arr = Array<number>(length).fill(value) return arr } function createStringArray (length: number, value: string): string[] { const arr = Array<string>(length).fill(value) return arr } function createArray<T> (length: number, value: T): T[] { const arr = Array<T>(length).fill(value) return arr } // const res = createNumberArray(3, 100) // res => [100, 100, 100] const res = createArray<string>(3, 'foo') 複製代碼
有一些三方的庫 引入不知道是什麼類型 可使用declare
lai 聲明類型
還有些第三方庫會有專門的類型聲明文件
import { camelCase } from 'lodash'
import qs from 'query-string' qs.parse('?key=value&key2=value2') // declare function camelCase (input: string): string const res = camelCase('hello typed') 複製代碼
參考