是 JavaScript 的一個超集,它能夠編譯成純 JavaScript。編譯出來的 JavaScript 能夠運行在任何瀏覽器上,主要提供了類型系統和對 ES6 的支持。node
.js
文件直接重命名爲.ts
便可TypeScript 只會在編譯的時候對類型進行靜態檢查,若是發現有錯誤,編譯的時候就會報錯。git
npm i -g typescript
tsc -v
我當前版本爲 Version 4.0.2github
tsc --init
"outDir": "./dist", "rootDir": "./src"
tsc -w
以後你寫的 ts 文件編譯錯誤都會直接提示,若是想運行文件,就到 /dist
目錄下找相應的 js 文件,使用 node 運行便可typescript
固然這樣其實也挺麻煩,咱們想直接運行 TS 文件, 這時能夠藉助ts-node
插件npm
全局安裝json
npm install -g ts-node
找到文件路徑,運行便可數組
ts-node demo.ts
let num: number = 2
let isShow: boolean = true
let str: string = 'hello'
let arr1: number[] = [1, 2, 3] let arr2: Array<number> = [2, 3, 4]
let foo: any = 'hello' foo = 12 foo = false
null 和 undefined 能夠賦值給任意類型的變量瀏覽器
let test1: undefined = undefined let test2: null = null let test3: number let test4: string test3 = null test4 = undefined
void 類型像是與 any 類型相反,它表示沒有任何類型。當一個函數沒有返回值時,其返回值類型是 void框架
let test5: void = undefined // 聲明一個 void 類型的變量沒有什麼用,由於它的值只能爲 undefined 或 null function testFunc(): void {} // 函數沒有返回值
never 類型表示的是那些永不存在的值的類型。dom
function bar(): never { throw new Error('never reach') }
全部類型均可以賦值給 any,全部類型也均可以賦值給 unknown。
let value: unknown value = 123 value = 'Hello' value = true let value1: unknown = value let value2: any = value let value3: boolean = value // Error let value4: number = value // Error let value5: string = value // Error let value6: object = value // Error let value7: any[] = value // Error let value8: Function = value // Error
unknown 類型只能被賦值給 any 類型和 unknown 類型自己。
數組合並了相同類型的對象,而元組(Tuple)合併了不一樣類型的對象。元組表示一個數量和類型都已知的數組
let tupleArr1: [string, number] = ['hello', 10] // let tupleArr2: [string, number] = [10, 'hello'] // Error
使用枚舉能夠定義一些帶名字的常量。 TypeScript 支持數字的和基於字符串的枚舉。
enum Season { Spring, Summer, Autumn, Winter, } let a: Season = Season.Spring let b: Season = Season.Summer let c: Season = Season.Autumn let d: Season = Season.Winter console.log(a, b, c, d) // 0 1 2 3 }
// JS function func1(x, y) { return x + y } // TS function func2(x: number, y: number): number { return x + y }
在 TypeScript 的類型定義中,=>
用來表示函數的定義,左邊是輸入類型,須要用括號括起來,右邊是輸出類型。
// JS let func3 = function (x, y) { return x + y } // TS 第一種方式 let func4 = function (x: number, y: number): number { return x + y } // TS 第二種方式 // => 用來表示函數的定義,左邊是輸入類型,須要用括號括起來,右邊是輸出類型。 let func5: (x: number, y: number) => number = function (x: number, y: number): number { return x + y }
採用函數表達式|接口定義函數的方式時,對等號左側進行類型限制,能夠保證之後對函數名賦值時保證參數個數、參數類型、返回值類型不變。
interface SearchFunc { (source: string, subString: string): boolean } let mySearch: SearchFunc mySearch = function (source: string, subString: string) { return source.search(subString) !== -1 }
對於 TS 的函數,輸入多餘的(或者少於要求的)參數,是不容許的,那麼該怎麼設置可選參數呢?,咱們能夠在聲明以後加一個問號
function getName1(firstName: string, lastName?: string) { // 可選參數必須接在必需參數後面 if (lastName) { return `${firstName} ${lastName}` } else { return firstName } } let name1 = getName1('jacky') let name2 = getName1('jacky', 'lin') console.log(name1, name2)
function getName2(firstName: string = 'monkey', lastName: string) { return `${firstName} ${lastName}` } console.log(getName2('jacky', 'Lin')) console.log(getName2(undefined, 'Lin')) // monkey Lin
當函數沒有返回值時,能夠用 void 來表示。
當一個函數永遠不會返回時,咱們能夠聲明返回值類型爲 never
function func6(): void { // return null } function func7(): never { throw new Error('never reach') }
function push1(arr, ...items) { items.forEach(function (item) { arr.push(item) }) } let a: any[] = [] push1(a, 1, 2, 3) function push2(arr: any[], ...items: any[]): void { items.forEach(function (item) { arr.push(item) }) } let b: any[] = [] push1(b, 1, 2, 3, '5')
// js寫法 function add({ one, two }) { return one + two } const total = add({ one: 1, two: 2 }) // ts 寫法 function add1({ one, two }: { one: number; two: number }): number { return one + two } const three = add1({ one: 1, two: 2 })
重載容許一個函數接受不一樣數量或類型的參數時,做出不一樣的處理。
function reverse(x: number): number // 函數定義 function reverse(x: string): string // 函數定義 function reverse(x: number | string): number | string { // 函數實現 if (typeof x === 'number') { return Number(x.toString().split('').reverse().join('')) } else if (typeof x === 'string') { return x.split('').reverse().join('') } }
TypeScript 容許你覆蓋它的推斷,而且能以你任何你想要的方式分析它,這種機制被稱爲「類型斷言」。
TypeScript 類型斷言用來告訴編譯器你比它更瞭解這個類型,你知道你本身在幹什麼,而且它不該該再發出錯誤。
interface Cat { name: string run(): void } interface Fish { name: string swim(): void } // 只能訪問共有的屬性 function getName(animal: Cat | Fish): string { return animal.name } // 將 animal 斷言成 Fish 就能夠解決訪問 animal.swim 時報錯的問題 function isFish(animal: Cat | Fish): boolean { if (typeof (animal as Fish).swim === 'function') { return true } return false } // 任何類型均可以被斷言爲 any ;(window as any).randomFoo = 1
// 類型斷言第一種方式:"尖括號"語法 let value1: any = 'hello' let value1Length: number = (<string>value1).length // 類型斷言第二種方式:as let value2: any = 'world' let value2Length: number = (value2 as string).length
class Animal { constructor(name: string) { this.name = name } // getter get name() { return '名字' } // setter set name(value: string) { console.log('setter: ' + value) } } let animal = new Animal('monkey') console.log(animal.name) // monkey animal.name = 'mk' // setter: mk
class Person { public name private age protected sex public constructor(name: string, age: number, sex: string) { this.name = name this.age = age this.sex = sex } } let person1 = new Person('jacky', 22, 'man') person1.name = 'monkey' // name值能夠訪問且修改 // person1.age // Property 'age' is private and only accessible within class 'Person'. // person1.sex // Property 'sex' is private and only accessible within class 'Person'. class Person1 extends Person { constructor(name: string, age: number, sex: string) { super(name, age, sex) // console.log(this.name, this.age, this.sex) // Property 'age' is private and only accessible within class 'Person'. } }
同時給類中定義屬性的同時賦值
class Animal3 { public name constructor(name: string) { this.name = name } } console.log(new Animal3('animal3').name) // animal3 class Animal2 { constructor(public name: string) {} // 簡潔形式 } console.log(new Animal2('animal2').name) // animal2
class Animal4 { readonly name constructor(name: string) { this.name = name } } let animal4 = new Animal4('animal4') // animal4.name = '5' // Cannot assign to 'name' because it is a read-only property
// 抽象類是行爲的抽象,通常來封裝公共屬性的方法的,不能被實例化 abstract class CommonAnimal { name: string abstract speak(): void }
class Animal5 { static sayHi() { console.log('Hello Animal5') } } Animal5.sayHi()
聯合類型(Union Types)表示取值能夠爲多種類型中的一種。使用一個|
分割符來分割多種類型
let foo: string | number | boolean foo = 'test' foo = 3 foo = true
interface IPerson { id: string age: number } interface IWorker { companyId: string } type IStaff = IPerson & IWorker const staff: IStaff = { id: '007', age: 24, companyId: '1' }
type Message = string | string[] let getMsg = (message: Message) => { return message } type Weather = 'SPRING' | 'SUMMER' | 'AUTUMN' | 'WINTER' let weather1: Weather = 'SPRING' let weather2: Weather = 'AUTUMN'
interface Person1 { name: string age: number } let person1: Person1 = { name: 'jacky', age: 23, }
interface AnimalLike { eat(): void move(): void } interface PersonLike extends AnimalLike { speak(): void } class Human implements PersonLike { speak() {} eat() {} move() {} }
class Animal1 { constructor(public name: string) {} age: number } class Animal2 { constructor(public age: number) {} } interface WithNameClass { new (name: string): Animal1 } function createClass(classname: WithNameClass, name: string) { return new classname(name) } let instance1 = createClass(Animal1, 'monkey') // let instance2 = createClass(Animal2, 'monkey') // 沒有name屬性則報錯
interface Person2 { readonly id: number [propName: string]: any //任意屬性 }
泛型是指定義函數、接口或類的時候,不預先指定具體的類型,而在使用的時候再指定類型的一種特性;一般用 T 表示,但不是必須使用改字母,只是常規,一般還有其餘經常使用字母:
class GenericNumber<T> { name: T add: (x: T, y: T) => T } let generic = new GenericNumber<number>() generic.name = 123
function func<T>(params: T[]) { return params } func<string>(['1', '2']) func<number>([1, 2])
function func1<T>(params: Array<T>) { return params } func1<string>(['1', '2']) func1<number>([1, 2])
能夠用來約束函數
interface Cart<T> { list: T[] } let cart: Cart<number> = { list: [1, 2, 3] }
type Cart2<T> = { list: T[] } | T[] let c1: Cart2<number> = { list: [1, 2, 3] } let c2: Cart2<number> = [1, 2, 3]
泛型接口 VS 泛型別名
// 不借助中間變量交換兩個變量的值 function swap<T, P>(tuple: [T, P]): [P, T] { return [tuple[1], tuple[0]] } let ret = swap([1, 'a']) ret[0].toLowerCase() ret[1].toFixed(2)
function createArray<T = number>(length: number, value: T): T[] { let arr: T[] = [] for (let i = 0; i < length; i++) { arr[i] = value } return arr } let arr = createArray(3, 9)
interface WithLength { length: number } // extends 來繼承 function logger<T extends WithLength>(val: T) { console.log(val.length) } logger('hello') logger([1, 2, 3]) // logger(true) // error 沒有length屬性
爲了方便開發者 TypeScript 內置了一些經常使用的工具類型,好比 Partial、Required、Readonly、Pick 等。它們都是使用 keyof 實現。
keyof 操做符能夠用來一個對象中的全部 key 值。
interface Person1 { name: string age: number sex?: string } type PersonKey = keyof Person1 // 限制 key 值的取值 function getValueByKey(p: Person1, key: PersonKey) { return p[key] }
// type Partial<T> = {[P in keyof T]?: T[P]} type PersonSearch = Partial<Person> // 所有變可選 // type Required<T> = { [P in keyof T]-?: T[P] } type PersonRequired = Required<Person> // 所有變必選 // type ReadOnly<T> = { readonly [P in keyof T]: T[P] } type PersonReadOnly = Readonly<Person> // 所有變只讀 // type Pick<T, K extends keyof T> = {[P in K]: T[P]} type PersonSub = Pick<Person, 'name'> // 經過從Type中選擇屬性Keys的集合來構造類型。
let root = document.getElementById('root') let children: HTMLCollection = root!.children // ! 表明非空斷言操做符 let childNodes: NodeListOf<ChildNode> = root!.childNodes
更明確的判斷某個分支做用域中的類型,主要嘗試檢測屬性、方法或原型,以肯定如何處理值。
function double(input: string | number | boolean): number { // 基本數據類型的類型保護 if (typeof input === 'string') { return input.length } else if (typeof input === 'number') { return input } else { return 0 } }
class Monkey { climb: string } class Person { sports: string } function getAnimalName(animal: Monkey | Person) { if (animal instanceof Monkey) { console.log(animal.climb) } else { console.log(animal.sports) } }
class Student { name: string play: string[] } class Teacher { name: string teach: string } type SchoolRole = Student | Teacher function getRoleInformation(role: SchoolRole) { if ('play' in role) { console.log(role.play) } if ('teach' in role) { console.log(role.teach) } }
好比有時兩個類型有不一樣的取值,也沒有其餘能夠區分的屬性
interface Bird { leg: number } interface Dog { leg: number } function isBird(x: Bird | Dog): x is Bird { return x.leg === 2 } function getAnimal(x: Bird | Dog): string { if (isBird(x)) { return 'bird' } else { return 'dog' } }
上述完整代碼示例:typescript-tutorial