1. 網站示例:http://vikingship.xyz/?path=/story/%E6%AC%A2%E8%BF%8E%E6%9D%A5%E5%88%B0%E8%AF%BE%E7%A8%8B--welcome
2. npm地址:https://www.npmjs.com/package/vikingship
======
1. 動態類型語言VS靜態類型語言
動態類型語言:執行時才去數據類型的檢查,一個變量可以是字符串,又可以改成數字【JS即是動態類型語言】,於是編寫一些檢查器,如eslint。
靜態類型語言:在編譯時就去執行數據類型檢查;
node版本:12.5.0
npm版本:6.9.0
安裝 typescript : npm install -g typescript@3.7.2
TS版本號: 3.7.2
每次改動ts文件,需要 tsc -w 監聽變化后 用node執行,需要兩步驟,所以使用 ts-node 合並了這兩個方法;
https://www.npmjs.com/package/ts-node
安裝之后,執行 ts-node a.ts 即可實時監聽文件變化並用node執行
let isDone: boolean = false let age: number = 20 let binaryNumber: number = 0b1111 let firstName: string = 'viking' let message: string = `Hello, ${firstName}, age is ${age}` let u: undefined = undefined let n: null = null //undefined 和 null 是所有類型中的子類型,在這里定義的是number類型,但是也可以賦值給 undefined 和 null let num: number = undefined let notSure: any = 4 notSure = 'maybe it is a string' notSure = true notSure.myName notSure.getName() //聯合類型,兩個類型都行 let numberOrString: number | string = 234 numberOrString = 'abc' // 數組 let arrOfNumbers: number[] = [1, 2, 3, 4] arrOfNumbers.push(5)//因為定義了類型是 數字的數組類型 所以可以推入5 arrOfNumbers.push('str')//則報錯 function test() { console.log(arguments) } //元組,可以定義數組包含多個類型,多一項和少一項,以及順序不對都不行 let user: [string, number] = ['viking', 1]
interface:
/* interface接口,對對象的形狀進行描述;對類class進行抽象 */ interface Person { readonly id: number;//只讀屬性,只有在創建的時候可以賦值 name: string;//用的是分號 age?: number;//?表示可選屬性 } let viking: Person = { id: 1234, name: 'viking', } /* viking.id = 111;則報錯 類似於const const用在變量上,readonly用在屬性上; */
函數:
// 函數聲明 // 可選函數z,相當於es6中的默認參數,function add(z:number=100){...} function add(x: number, y: number, z?: number): number {//z可有可無,可選參數必須放在最后面 if (typeof z === 'number') { return x + y + z } else { return x + y } } let result = add(2, 3, 5) //函數表達式 const add1 = function(x: number, y: number, z: number = 10): number { if (typeof z === 'number') { return x + y + z } else { return x + y } } const add3:number = add1;//這樣會報錯。因為add1也有類型 const add2: (x: number, y: number, z?: number) => number = add1//=>箭頭不是es6中的箭頭函數,而是ts中函數返回值類型
類:
類有三大特征: 封裝、繼承、多態
多態指的是 子類繼承父類 實例化同一個函數方法為不同的方法,比如貓和狗兩個子類都繼承了動物類,但是他們實現的eat的方法不同
class Animal { name: string; static categoies: string[] = ['mammal', 'bird'] static isAnimal(a) { return a instanceof Animal } constructor(name: string) { this.name = name } run() { return `${this.name} is running` } } console.log(Animal.categoies) const snake = new Animal('lily') console.log(Animal.isAnimal(snake)) //類的繼承 class Dog extends Animal { bark() { return `${this.name} is barking` } } const xiaobao = new Dog('xiaobao') //類的多態,繼承的子類,重寫父類中的方法 class Cat extends Animal { constructor(name) { super(name) console.log(this.name) } run() { return 'Meow, ' + super.run() } } const maomao = new Cat('maomao') /* 使用 public、private、protected 使用 public 外部可以訪問到,private子類也無法訪問,protected其子類可以訪問到 readonly--設置屬性只可以讀,無法改動 */ class Animal2 { readonly name: string;//這樣設置屬性,只可以讀,無法改動 constructor(name: string) { this.name = name } run() { return `${this.name} is running` } } const snake2 = new Animal2('lily'); snake2.name = 'lili';//無法改動 /* 靜態屬性,不用實例化,可以在class上直接訪問,因為它於其他屬性方法沒有關系 */ class Animal3 { name: string; static categoies: string[] = ['mammal', 'bird'] static isAnimal(a) {//靜態方法 return a instanceof Animal } constructor(name: string) { this.name = name } run() { return `${this.name} is running` } } console.log(Animal3.categoies);//訪問類的靜態屬性,不用實例化類,直接就可以訪問 /* interface——對對象和類的行為進行抽象定義,比如兩個類Car2、 Cellphone2都要實現一個方法switchRadio, 則可以定義一個 interface,規定好 switchRadio 方法的類型,然后 implements 后 就必須要實現才行 */ interface Radio2 { switchRadio(): void; } class Car2 implements Radio2{ switchRadio() { } } class Cellphone2 implements Radio2 { switchRadio() { } } /* 類似的,如果要同時實現多個接口定義,比如 Cellphone 有個方法是 檢查電池容量 */ interface Radio3 { switchRadio(): void; } interface Battery3 { checkBatteryStatus(); } class Cellphone3 implements Radio3, Battery3 { switchRadio() { } checkBatteryStatus() { } } /* 接口之間還有繼承關系*/ interface Radio { switchRadio(): void; } interface Battery { checkBatteryStatus(); } interface RadioWithBattery extends Radio { //interface 繼承了 Radio checkBatteryStatus(); } class Car implements Radio{ switchRadio() { } } class Cellphone implements RadioWithBattery {//實現的時候需要實現兩個方法 switchRadio() { } checkBatteryStatus() { } }
枚舉
/* 枚舉,默認會被賦值給0開始的數字 */ enum Direction2 { Up, Down, Left, Right } console.log(Direction2.Up);//0 console.log(Direction2[0]);//反向映射,可以看作是一個數組來取值 /* 也可以手動賦值,則剩下未賦值的默認遞增,比如下面的例子,Down就是11 */ enum Direction3 { Up=10, Down, Left, Right } //const 常量枚舉,提升性能,編譯后只是一個常量,剩下 Direction.up = "UP" const enum Direction { Up = 'UP', Down = 'DOWN', Left = 'LEFT', Right = 'RIGHT', } const value = 'UP' if (value === Direction.Up) { console.log('go up!') }
范型:
/* <>范型,一般定義函數,對象; 比如echo函數可以傳入多個類型的參數,但是輸出也要對應類型; 這樣不能設置為any,因為會喪失類型檢查,所以設置了<T>范型 注意T只是一個常規寫法,不一定非要是T,相當於一個類型的占位符 echo定義了范型<T>,入參arg:T 返回的也是T 這樣 const result = echo(true)//result就是boolean類型 const result = echo(123)//result就是123數字類型 也就是echo函數在定義的時候沒有指明具體類型,但是在使用的時候 才去確定 輸入類型和輸出類型 */ function echo<T>(arg: T): T { return arg } const result = echo(true) //使用范型,定義多個參數類型,類型是對應的,如下進行轉換 function swap<T, U>(tuple: [T, U]): [U, T] { return [tuple[1], tuple[0]] } const result2 = swap(['string', 123]) /* 如果不在范型后面增加類型限制,函數中無法獲取arg.length; 所以要在入參的范型中增加[]數組的定義 但是這樣只能輸入數組類型,比如字符串也有長度,確無法使用該范型定義的函數 const arrs = echoWithArr('string')//報錯 */ function echoWithArr<T>(arg: T[]): T[] { console.log(arg.length) return arg } const arrs = echoWithArr([1, 2, 3]) //約束范型 interface IWithLength { length: number //規定必須有length屬性 } function echoWithLength<T extends IWithLength>(arg: T): T { console.log(arg.length) return arg } const str = echoWithLength('str') const obj = echoWithLength({ length: 10, width: 10}) const arr2 = echoWithLength([1, 2, 3]) //范型在類中的使用,實現一個隊列類,實現兩個方法 /* 如下面的例子,類沒有定義TS類型,則默認是any; 如果入參的是 字符串類型,則console時,找不到其toFixed, 進而運行時報錯 */ class Queue2 { private data = []; push(item) { return this.data.push(item) } pop() { return this.data.shift() } } const queue3 = new Queue2() queue3.push(1); queue3.push('str'); console.log(queue3.pop().toFixed()) //給類定義范型,規定輸入和輸出的類型一樣 class Queue<T> { private data = []; push(item: T) { return this.data.push(item) } pop(): T { return this.data.shift() } } //在實例化類的時候,定義具體的類型 const queue = new Queue<number>() queue.push(1) console.log(queue.pop().toFixed()) const queue2 = new Queue<string>() queue2.push('str') console.log(queue2.pop().length) //interface接口也可以使用范型 interface KeyPair<T, U> { key: T; value: U; } //則在具體使用的時候,定義其類型 let kp1: KeyPair<number, string> = { key: 123, value: "str" } let kp2: KeyPair<string, number> = { key: 'test', value: 123 } let arr: number[] = [1, 2, 3] let arrTwo: Array<number> = [1, 2, 3] //函數使用范型,規定使用該范型的函數,需要保證輸入輸出的類型 interface IPlus<T> { (a: T, b: T) : T } function plus(a: number, b: number): number { return a + b; } function connect(a: string, b: string): string { return a + b } const a: IPlus<number> = plus const b: IPlus<string> = connect
類型斷言
// type aliases 類型別名 type PlusType = (x: number, y: number) => number//在這里定義類型 function sum(x: number, y: number): number { return x + y } const sum2: PlusType = sum//在這里直接使用 type NameResolver = () => string//定義函數類型 type NameOrResolver = string | NameResolver//定義聯合類型,可以輸入函數和字符串 function getName(n: NameOrResolver): string { if (typeof n === 'string') { return n } else { return n() } } // type assertion 類型斷言 function getLength(input: string | number) : number { //不能直接訪問 input.length的屬性,因為上面規定了是 string和number //的類型,所以只剩下這兩個類型的公共屬性 // const str = input as String//S大寫,斷言 // if (str.length) { // return str.length // } else { // const number = input as Number // return number.toString().length // } //下面是更簡單的方法,<string>input就是斷言input是字符串類型 if((<string>input).length) { return (<string>input).length } else { return input.toString().length } }
聲明文件.d.ts文件
聲明文件必須是以.d.ts為后綴【因為ts會解析項目中所有的ts后綴文件】
比如使用jquery('#name');編譯的時候會報錯,因為ts不知道jquery是個什么。
所以要新建一個聲明文件:jquery.d.ts, 則所有的ts文件均可以使用這個聲明文件
`declare var jQuery:(selector:string) => any`
@types 下面有很多第三方庫的聲明文件
====
分為三種情況:
1. 常規的js文件,然后使用cdn的方式在html中引入后,在全局引用時抱錯:
function globalLib(options){ console.log(options); } globalLib.version = '1.0.0'; globalLib.doSomeing = function(){ console.log('globalLib do something'); }
declare function globalLib(options:globalLib.Options):void; declare namespace globalLib{ const version:string; function doSomething():void; interface Options { //interface接口,對對象的形狀進行描述;對類class進行抽象 [key:string]:any } }
2.使用模塊,也就是module.exports = xxx 的形式
const version = '1.0.0'; function doSomething(){ console.log('globalLib do something'); } function moduleLib(options){ console.log(options); } moduleLib.version = version; moduleLib.doSomething = doSomething; module.exports = moduleLib;
import moduleLib from './module-lib';//如果不加TS聲明文件,這里會抱錯
declare function moduleLib(options:Options):void interface Options{ [key:string]:any } declare namespace moduleLib{ const version : string, function doSomething():void } export moduleLib
import m from 'moment'; declare module 'momoent' { export function myFunction():void } m.myFunction = ()=> {}
在webpack中使用TS-loader來編譯:
module:{ rules:[ { test:/\.tsx?$/i, use:[ { loader:'ts-loader', options:{ transpileOnly:true //關閉的時候可以提高構建速度,開啟后會失去類型檢查 } } ], exclude:/node_modules/ } ] }
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); { plugins:[ new ForkTsCheckerWebpackPlugin(); ] }
使用ESLint進行檢查:
TS和ESLint的區別和聯系
TS: 類型檢查+語言轉換+語法錯誤
ESLint: 代碼風格+語法錯誤
{ "devDependencies":{ "eslint":"^5.16.0", "@typescript-eslint/eslint-plugin":"^1.13.0",//能夠使eslint識別一些ts的特殊語法 "@typescript-eslint/parser":"^1.13.0"//為eslint提供解析器 } }
{ "parser":"@typescript-eslint/eslint-plugin", //規定eslint解析器 "plugins":["@typescript-eslint"], "parserOptions":{ "project":"./tsconfig.json" //ts 的類型信息 }, "extends":[ "plugin:@typescript-eslint/recommended"//規定使用eslint是官網提供的規則 ], "rules":{ "@typescript-eslint/no-inferrable-types":"off" //可以在這里關閉具體的某個規則 } }
{ "scripts":{ "lint":"eslint src --ext .js,.ts"//檢查以js和ts為后綴的文件 } }
{ "jsx":"react"//意思是把jsx轉成js文件 /*一共有三個值: 1. preserve:生成的代碼會保留jsx格式,文件的擴展名就是jsx,可以方便后續 使用,比如傳遞給babel 2. react-native:生成的文件是jsx格式,但是擴展名是js 3. react:轉成純js語法的文件 */ }
對於Redux,沒有特別的,需要注意一下類型聲明即可
const mapStateToProps = (state:any)=>{ comploy:state.data.list }; const mapDIspatchToProps = (dispatch:Dispatch)=> { onGetList:getEmployee }