春節間的TypeScript筆記整理


寫在前面

因爲在春節期間,實在沒什麼事幹,就想系統的寫份typescript筆記。廢話很少說,爲何咱們都在說着ts這件事呢,尤爲對於前端開發者來講,JavaScript做爲行爲交互,是四劍客之一。那麼爲何還要用ts呢?javascript

現狀分析

咱們知道,目前國內主流的3大前端開發框架:html

框架名稱 version github star
React V16.12.0 142,715
Vue.js V2.x 155,969
Angular 56,653

tips:這裏暫不說跨平臺解決方案,好比比較優秀的Flutter等。 固然了咱們會有一個疑問,假若咱們用ts編碼,會不會現有的框架會不支持,這點我們大可沒必要擔憂。前端

那咱們想再看下,當前比較流行的框架、庫,它們的源碼狀況vue

  • Redux

  • mobx

正是因爲java

  • 優秀的第三方框架庫ts的比重愈來愈多
  • 即將發佈的Vue.js 3x 版本,源碼用ts重構
  • 老舊的項目能夠無縫銜接ts

以及咱們衆所周知的JavaScript自己存在的一些不夠完美的地方,例如xxx 未定義,等等。固然了在一些原有的項目中可能也不習慣從新接觸ts,那麼我和你們一塊來來作作筆記,這對於咱們仍是頗有必要的。node

環境準備

  • 編輯器 這裏推薦很好用的Vscode ,本筆記相關代碼也是用此react

  • 相關環境版本git

    • node
    • npm
    • vue/cli
  • 第三方輔助github

      • ts-node@8.6.2 TypeScript execution and REPL for node.js
    npm install -g ts-node
    複製代碼

    具體使用參考 ts-node xxx.tstypescript

    • nodemon@2.0.2
    npm i -g nodemon
    複製代碼

筆記大綱

看過我其餘筆記的掘友,知道我是剛搞了臺顯示器,那電腦仍是用的比較陳舊的不靈敏的mac,因此可能會少些xmind 圖什麼的,但願您也能看下去,這麼無聊的文字。筆記主要分爲三部分

前期思路

JavaScript vs TypeScript

首先,咱們不論是讀什麼文章,或者茶餘飯後同事嘮嗑,多少都有耳聞,ts相比於js會有幾點不一樣

  • 真正意義上的面向對象編程
  • 類型校驗
  • 是js 的超集
  • ...

對這幾點對於編程來講仍是尤其重要的,咱們先看幾個簡單的demo 一些簡單的瀏覽器調試,我們選擇在vscode裏直接預覽,這裏推薦一個插件

  • Brower Previews

具體的使用方法可參考文末

  1. 聲明一個變量,並賦值

就很奇怪,咱們命名想讓 aString是一個字符串,可又能從新賦值爲數字

  1. 定義一個求和功能函數
let aString = `我是一個字符串`
aString = 123
// alert(aString)
console.log(aString)
const addFn = (num1, num2) => alert(num1 + num2)
console.log(1, 2)
console.log('1', '2')
console.log('1', 2)
console.log(1)
複製代碼

咱們能夠發現,運行的時候才知道咱們出現了參數的問題,那麼這未嘗不是咱們頭疼的一個問題呢,就像是

undefined.fliter()
複製代碼

本節小結

能夠簡單的體會到如下幾點

  • 始於js,歸於js
  • 弱類型漸漸有點強
  • 社區更新的很是快
  • 適合大型項目的開發
  • 更好儘早的避免BUG
  • 可以少些一些註釋 例如咱們的一些方法,能夠減小相關的文檔註釋
/** * * @param {string} name * @param {number} age * @param {string} sex */
const Me = (name, age, sex) => `個人名字是${name},我今年${age},我是${sex}生,歡迎關注個人掘金呦~`
console.log(Me(`洋小洋同窗`, 18, `男`))
=>個人名字是洋小洋同窗,我今年18,我是男生,歡迎關注個人掘金呦~

複製代碼
  • IDE良好的提示(這裏咱們後續慢慢體會下)

學習文檔

hello-world

老規矩,我們一塊寫個hello world

const hello = (): string => `hello TS`

複製代碼

咱們知道瀏覽器是不支持ts代碼的(目前),那麼咱們該如何編輯一下呢

安裝

> npm install -g typescript
複製代碼

+ typescript@3.7.5 這樣咱們就能夠經過tsc xxx.ts來進行編譯爲js代碼

var hello = function () { return "hello TS"; }; // js代碼
複製代碼

watch 監聽

這裏咱們能夠經過tsc -w xx.ts 來監聽ts文件的變化並編譯

遇到問題

在咱們基礎調試的過程當中,會遇到 was also declared here

解決

類型

這裏我們知道:JS數據類型分類和判斷 JavaScript中有6種數據類型:數字(number)、字符串(string)、布爾值(boolean)、undefined、null、對象(Object)。 其中對象類型包括:數組(Array)、函數(Function)、還有兩個特殊的對象:正則(RegExp)和日期(Date)。 那麼在ts中,定義變量

let a: number
a = 123 // 其中a 只能夠是number 類型

複製代碼

任意類型

ts中有一種類型,叫作任意類型any

  • 儘量少的使用any
let list: any[] = [1, true, "free"];

list[1] = 100;
複製代碼
const myInfoFn = (info: any) => {
    switch (typeof info) {
        case `number`:
            console.log(`我今年${info}`)
        case `string`:
            console.log(`個人名字${info}`)
        default:
    }

}

myInfoFn(`yayxs`)
複製代碼

數組

在實際的開發中,常常打交道的即是數組,那麼在ts中有兩種經常使用定義數組的方式(其中數組的每項元素是同一類型)

let arrSt: string[] = [`yayxs`]  // 每一項是string
let arrNum: Array<number> = [1, 2, 3] // 每一項是number
複製代碼

元組

  • 元素的個數固定
  • 順序儘可能不變
let tuple: [string, number, string]
tuple = [`yayxs`, 18, `男生`]
console.log(`${tuple[0]}${tuple[2]},今年${tuple[1]}`)
複製代碼

函數

函數無非是參數``返回值等,其中包括默認參數,可選參數等

const addFn = (a: number, b: number): number => a + b

const addCon = (a: number, b: number): void => console.log(a+b) // 函數沒有返回值

const addFn = (a: number, b: number = 10): number => a + b
// 參數默認值 b默認值值10

複製代碼

可選參數

const addABC = (a: number, b: number, c?: number): number => {
    if (c) {
        return a + b + c
    } else {
        return a + b
    }
}

複製代碼

不肯定參數 口語中的不肯定參數又叫剩餘參數,示例:

const arrList: Array<number> = [1, 2, 3, 4];
// 其中acc爲累加器;cur當前值
const reducer = (acc, cur): number => acc + cur;
let res: number = arrList.reduce(reducer, 5)
console.log(res) // 15


const addFun = (a: number, ...nums: number[]) => nums.reduce(reducer, a)
console.log(addFun(1, 2, 3, 4)) // 10
複製代碼

class MyGirlFriend {
    // 定義數據內容
    name: string;
    age: number;
    height: string
}
// new 對象
let xuehua = new MyGirlFriend()
// set 內容
xuehua.name = `xuehua` // 雪花
// get 內容
console.log(xuehua.name)
複製代碼
class MyGirlFriend {
    // 定義數據內容
    name: string;
    age: number;
    height: string


    constructor(name: string, age: number, height: string) {
        this.name = name
        this.age = age
        this.height = height
    }
    formatHeight() {
        return `${this.height} cm`
    }
}
// new 對象
let xuehua = new MyGirlFriend(`xuehua`, 20, `172`)

console.log(xuehua.name) // xuehua
console.log(xuehua.age) // 20
console.log(xuehua.formatHeight()) // 172cm

複製代碼

咱們能夠看下編譯爲js以後的代碼

var MyGirlFriend = /** @class */ (function () {
    function MyGirlFriend(name, age, height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }
    MyGirlFriend.prototype.formatHeight = function () {
        return this.height + " cm";
    };
    return MyGirlFriend;
}());
// new 對象
var xuehua = new MyGirlFriend("xuehua", 20, "172");
console.log(xuehua.name);
console.log(xuehua.age);
console.log(xuehua.formatHeight());
複製代碼

繼承

咱們都知道面向對象``繼承``多態,那麼什麼是繼承嘞:

/// 繼承Demo
class Person {
    // 數據-屬性
    name: string
    age: number
    constructor(name: string, age: number) {
        this.name = name
        this.age = age
    }
    // 行爲-方法
    sayHi() {
        console.log(`HI`)
    }
}

// 繼承Person
class Programmer extends Person {
    sayNo() {
        console.log(`NO`)
    }
    sayHi() {
        // console.log(`en~~~`)
        super.sayHi() // 調用父類
    }
}

let p = new Programmer(`yayxs`, 18)

p.sayHi()
複製代碼

相應的js代碼

var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
/// 繼承Demo
var Person = /** @class */ (function () {
    function Person(name, age) {
        this.name = name;
        this.age = age;
    }
    // 行爲-方法
    Person.prototype.sayHi = function () {
        console.log("HI");
    };
    return Person;
}());
// 繼承Person
var Programmer = /** @class */ (function (_super) {
    __extends(Programmer, _super);
    function Programmer() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    Programmer.prototype.sayNo = function () {
        console.log("NO");
    };
    Programmer.prototype.sayHi = function () {
        // console.log(`en~~~`)
        _super.prototype.sayHi.call(this);
    };
    return Programmer;
}(Person));
var p = new Programmer("yayxs", 18);
p.sayHi();

複製代碼

抽象類

抽象類不可以被實例化,可是能夠被繼承,一般要完成方法的實例化

// 抽象類

abstract class Person {
    name: string
    constructor(name: string) {
        this.name = name
    }
    sayHi(name: string): void {
        console.log(`hi`)
    }
    // 抽象方法 沒有方法體
    abstract work(): void
}

// let p = new Person() err
class Coder extends Person {
    sayHi() {
        console.log(`12131`)
    }
    work() {
        console.log(`i am working`)
    }
}
複製代碼

修飾符

  • public

    • 默認就是public 修飾
    • 公有,外部也可訪問
    • 繼承的對象也能使用
  • private

    • 私有的,內部才能訪問
  • protected

    • 受保護的,權限稍大於 private

其實在類的內部,全部的屬性方法默認是經過public修飾符來進行修飾的

class Person {
    public name:string  // 默認都是公開的
}
複製代碼

關於ts中的修飾符在其餘語言中也是相似的,例如Java等。

// 公共,私有與受保護的修飾符
class Person {
    public name: string
    private age: string

    public sayName() {
        console.log(`my name is ${this.name}`)
    }
    private sayAge() {
        console.log(`my age is ${this.age}`)
    }
}
複製代碼

那麼經過private 修飾的方法,在外部該怎麼訪問到呢?

getName() {
        console.log(this.name)
    } // 獲取名字
    
let p = new Person()
p.getName() // undefined


複製代碼
getName() {
        console.log(this.name)
    }
    setName(name: string) {
        this.name = name
    }
let p = new Person()
p.setName(`yayxs`)
p.getName() // yayxs
複製代碼

constructor

當被protected 修飾後,是不能new的,也就是說不能實例化

靜態屬性&方法

經過static 修飾的屬性或者方法,能夠直接經過類.xxx,

class Person {
    public static _name: string;

    public static getName(): string {
        return Person._name
    }
}

console.log(Person.getName())
複製代碼

本節小結

ts中類的實踐仍是十分有意義的,包括像靜態屬性方法,或者像public protected private 這些修飾符修飾在屬性以及方法前邊,主要是權限的限制,大體是 public > protected > private 具體的修飾細則,包括像 在父類以及子類的調用權限問題,能夠翻閱相關的文檔

  • 靜態成員更像是存在於類的自己,而不是new出來的實例對象上,這點官網也有說到

枚舉

首先不得不提的是枚舉在實際開發的過程當中,仍是十分有益的,可以加強代碼的可讀性,筆者在封裝網絡請求類或者網頁中常見的下拉選擇框有用到

enum RequestType {
    GET, POST, PUT, DELETE
}
console.log(RequestType.DELETE) // 3
複製代碼

之於爲何是3

var RequestType;
(function (RequestType) {
    RequestType[RequestType["GET"] = 0] = "GET";
    RequestType[RequestType["POST"] = 1] = "POST";
    RequestType[RequestType["PUT"] = 2] = "PUT";
    RequestType[RequestType["DELETE"] = 3] = "DELETE";
})(RequestType || (RequestType = {}));
console.log(RequestType.DELETE);

複製代碼

接口

接口在實際生活中也是無處不在的,像水龍頭與水管 插板與插槽 等等,那麼在程序中也是同樣的,同一接口規範了一類

// 接口
interface HasName {
    name: string
}
// 定義對象
const obj = {
    name: `yayxs`
}
// 定義方法

const sayName = (o: HasName) => {
    console.log(`my name is ${o.name}`)
}

sayName(obj) // my name is yayxs
複製代碼

以上的接口示例是隻有參數,那接口裏也是能夠定義方法的,就像這樣

// 接口
interface HasName {
    name: string
    printName(name: string): void
}
// 定義對象
const obj = {
    name: `yayxs`,
    printName: (name: string) => {
        console.log(name)
    }
}
// 定義方法

const sayName = (o: HasName) => {
    console.log(`my name is ${o.name}`)
    o.printName(o.name) // yayxs
}

sayName(obj)
複製代碼

可選參數

在定義接口的時候,咱們能夠定義一部分可選擇的屬性或方法,在實現接口的時候不是非要實現

/// 可選參數
type PerInfo = string
interface Person {
    name: PerInfo
    age?: number
    sex: PerInfo
    printName(): void
    printAge?(): void
}
// 我實現人類接口
class Me implements Person {
    printName(): void {
        console.log(`xxx`)
    }
    name: string
    sex: string
}

let m = new Me()
複製代碼

類型別名

類型別名,顧名思義就是類型的別名

// 類型別名
type Name = string; // 把string別名爲Name 更語義化一點

const myName: Name = `yayxs`

console.log(myName)
複製代碼

又或者

type User = {
    name: string,
    age: number,
    sex: string
}

const me: User = {
    name: `yayxs`,
    age: 18,
    sex: `nan`
}

console.log(me)
複製代碼

那麼類型別名接口有點類似

  • 類型別名不能夠重複定義,而接口是能夠的
  • 接口經常使用些

類與接口

// 定義支付接口
interface Pay {
    doSomething(): void // 支付接口方法
}

const myPay = () => {
    // 實現接口中的pay()
    doSomething: () => {
        console.log(`實現支付動做`)
    }
}

// 定義不一樣的類來實現接口
class WxPay implements Pay {
    doSomething() {
        console.log(`我是微信支付`)
    }
}
class AlPay implements Pay {
    doSomething() {
        console.log(`我是支付寶支付`)
    }
}

let weixin_pay: Pay = new WxPay()
let ali_pay: Pay = new AlPay()
複製代碼

匿名函數接口

在一些接口的內部聲明的函數是沒有名字的

// 匿名函數接口

interface Person {
    (num: number): void
}

let printNum: Person;

printNum = (num: number) => {
    console.log(num)
}
複製代碼

類型斷言

包括像是在dart語言內,都會有斷言存在

  • 主要是在編譯時期
  • 告訴編譯器是什麼類型
// 類型斷言
let x: any = `hello`
let res = (<string>x).substring(0, 1)
console.log(res)
複製代碼

在實際開發中書寫斷言幾種常見的方式

interface Person {
    name: string
    age: number
}

let me = {} as Person
me.name = `yayxs`
me.age = 18
複製代碼
let you = <Person>{
    name: `xuehua`,
    age: 17
}
複製代碼

接口繼承

類不能繼承多個父類,可是能夠實現多個已定義的接口

interface Person {
    name: string
}
interface Coder {
    age: number
}

class Per implements Person, Coder {
    name: string
    age: number
} 
複製代碼

可索引類型

// 可索引類型
interface Istr {
    [index: string]: string
}

let myStr: Istr;
myStr = {
    'name': `yayxs`
}
interface Inum {
    [index: number]: string
}
let myNum: Inum
myNum = [`12`]

複製代碼

在如上第二個例子中,有點像數組的定義方式,那與數組有什麼不一樣呢

  • 可索引類型沒有數組的相關方法和屬性length或者push等常見的方法

類型「Inum」上不存在屬性「length」。

getter && setter

在其餘的編程語言中,獲取屬性與設置也一樣的有這種狀況 在前面部分咱們提到過

這樣的話,就遇到一個問題,沒法讀取name屬性,這時候能夠經過內部方法暴露出來,那一樣在類中有get set

// setter and getter
class Person {
    private name: string
    private age: number
    constructor(age: number, name: string) {
        this.name = name
        this.age = age
    }
    get getName(): string {
        return this.name
    }
    set setName(name: string) {
        this.name = name
    }
}

let p = new Person(18, `yayxs`)
// console.log(p.name)

複製代碼

函數

函數重載

js中是沒有函數重載的,那麼在ts中什麼是函數重載呢,

  • 意思就是先定義方法,但不實現方法體
  • 函數的名稱能夠重名,並不會被覆蓋
// 函數重載
function hello(name: string): string function hello(age: number): number function hello(params: any): any { if (typeof params === `string`) { return params } else if (params === `number`) {
        return params
    }
}
複製代碼

非空檢查

咱們能夠經過!來進行非空的檢查

let str: string = `yayxs`;
str!.substring(0, 1)
複製代碼

never

  • 一種狀況是,不期待有返回結果
function loop():never{
    while(true){
        
    }
}
複製代碼
  • 一種是內部拋出異常
function err(): never {
    throw Error
}
複製代碼

泛型

在其餘的語言中像JAVA中有泛型的概念,

// 泛型

function getArr<T>(params: T[]): T[] {
    return params
}
console.log(getArr<number>([22, 1]))

class Person<S, N> {
    private _name: S
    private _age: N
    constructor(name: S, age: N) {
        this._name = name
        this._age = age
    }
    get name(): S {
        return this._name
    }
    get age(): N {
        return this._age
    }
}
let p = new Person<string, number>(`yaxs`, 18)

console.log(p.name)
複製代碼

在編譯的時候請使用

tsc --target es5 demo12.ts
複製代碼

模塊化

能夠經過兩種方式導出ts中的元素

  • module default export
  • export

tsconfig.json

能夠經過tsc --init來生成這麼一個文件

{
  "compilerOptions": {
    /* Basic Options */
    // "incremental": true,                   /* Enable incremental compilation */
    "target": "es5",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
    "module": "commonjs",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
    // "lib": [],                             /* Specify library files to be included in the compilation. */
    // "allowJs": true,                       /* Allow javascript files to be compiled. */
    // "checkJs": true,                       /* Report errors in .js files. */
    // "jsx": "preserve",                     /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
    // "declaration": true,                   /* Generates corresponding '.d.ts' file. */
    // "declarationMap": true,                /* Generates a sourcemap for each corresponding '.d.ts' file. */
    // "sourceMap": true,                     /* Generates corresponding '.map' file. */
    // "outFile": "./",                       /* Concatenate and emit output to single file. */
    // "outDir": "./",                        /* Redirect output structure to the directory. */
    // "rootDir": "./",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
    // "composite": true,                     /* Enable project compilation */
    // "tsBuildInfoFile": "./",               /* Specify file to store incremental compilation information */
    // "removeComments": true,                /* Do not emit comments to output. */
    // "noEmit": true,                        /* Do not emit outputs. */
    // "importHelpers": true,                 /* Import emit helpers from 'tslib'. */
    // "downlevelIteration": true,            /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
    // "isolatedModules": true,               /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */

    /* Strict Type-Checking Options */
    "strict": true,                           /* Enable all strict type-checking options. */
    // "noImplicitAny": true,                 /* Raise error on expressions and declarations with an implied 'any' type. */
    // "strictNullChecks": true,              /* Enable strict null checks. */
    // "strictFunctionTypes": true,           /* Enable strict checking of function types. */
    // "strictBindCallApply": true,           /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
    // "strictPropertyInitialization": true,  /* Enable strict checking of property initialization in classes. */
    // "noImplicitThis": true,                /* Raise error on 'this' expressions with an implied 'any' type. */
    // "alwaysStrict": true,                  /* Parse in strict mode and emit "use strict" for each source file. */

    /* Additional Checks */
    // "noUnusedLocals": true,                /* Report errors on unused locals. */
    // "noUnusedParameters": true,            /* Report errors on unused parameters. */
    // "noImplicitReturns": true,             /* Report error when not all code paths in function return a value. */
    // "noFallthroughCasesInSwitch": true,    /* Report errors for fallthrough cases in switch statement. */

    /* Module Resolution Options */
    // "moduleResolution": "node",            /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
    // "baseUrl": "./",                       /* Base directory to resolve non-absolute module names. */
    // "paths": {},                           /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
    // "rootDirs": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */
    // "typeRoots": [],                       /* List of folders to include type definitions from. */
    // "types": [],                           /* Type declaration files to be included in compilation. */
    // "allowSyntheticDefaultImports": true,  /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
    "esModuleInterop": true,                  /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
    // "preserveSymlinks": true,              /* Do not resolve the real path of symlinks. */
    // "allowUmdGlobalAccess": true,          /* Allow accessing UMD globals from modules. */

    /* Source Map Options */
    // "sourceRoot": "",                      /* Specify the location where debugger should locate TypeScript files instead of source locations. */
    // "mapRoot": "",                         /* Specify the location where debugger should locate map files instead of generated locations. */
    // "inlineSourceMap": true,               /* Emit a single file with source maps instead of having a separate file. */
    // "inlineSources": true,                 /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */

    /* Experimental Options */
    // "experimentalDecorators": true,        /* Enables experimental support for ES7 decorators. */
    // "emitDecoratorMetadata": true,         /* Enables experimental support for emitting type metadata for decorators. */

    /* Advanced Options */
    "forceConsistentCasingInFileNames": true  /* Disallow inconsistently-cased references to the same file. */
  }
}

複製代碼

具體的註釋的含義,能夠參考官方文檔

傾情連接

若是你尚未看過癮,在19年度我曾寫過一篇很簡單的文章,也是基於第三方的環境搭建的一篇極爲簡單的博客(依賴github、Vuepress)

寫在最後

相關代碼已經上傳至 筆者github ,也歡迎指正不恰當的地方,感謝~

參考文章

相關文章
相關標籤/搜索