首頁 > 軟體

Typescript型別系統FLOW靜態檢查基本規範

2022-05-17 13:01:09

Typescript是一門基於JavaScript之上的語言,重點解決了JavaScript自由型別系統的問題。

使用Typescript可以大大提高程式碼的可靠程度。

型別系統

強型別和弱型別(型別安全)

強型別:語言層面限制函數的實參型別必須與形參型別相同

弱型別:語言層面不會限制實參的型別

由於這種強弱型別之分根本不是某一個權威機構的定義,所以導致後人對這種鑑定方式的細節,出現了不一致的理解。但整體上大家的鑑定方式都是在描述強型別有更強的型別約束,而弱型別中幾乎沒有什麼約束。

強型別語言中不允許有任意的隱式型別轉換,而弱型別語言中則允許任意的資料隱式型別轉換。

強型別不允許隨意的隱式型別轉換,而弱型別則是允許的。

變數型別允許隨時改變的特點,不是強弱型別的差異。

靜態型別與動態型別(型別檢查)

  • 靜態型別:一個變數宣告時它的型別就是明確的,而且宣告過後,它的型別就不允許再修改。
  • 動態型別:執行階段才能夠明確變數型別。動態型別語言中的變數沒有型別,變數中存放的值是有型別的。

JavaScript就是動態型語言,而且變數的型別隨時可以改變。

常用程式語言:

JavaScript自由型別系統的問題

JavaScript是弱型別且動態型別,甚至可以說它沒有型別。它的特徵就是[任性]、[不靠譜],缺失了型別系統的可靠性。

JavaScript沒有編譯環節

大規模應用下,弱型別/動態型別這種優勢就變成了短板。

弱型別的問題:君子約定有隱患,強制要求有保障。

強型別的優勢:

  • 錯誤更早暴露
  • 程式碼更智慧,編碼更準確
  • 重構更牢靠
  • 減少不必要的型別判斷

Flow靜態型別檢查方案

  • Flow是JavaScript的靜態型別檢查器。
  • 彌補了JavaScript的不足,為JavaScript提供了更完善的型別系統。
  • 工作原理就是通過在程式碼當中新增一些型別註解方式來去標記程式碼中變數或引數應該是什麼型別的。
//@flow
function sum(a: number, b: number) {
    return a + b
}
console.log(sum(10, 20));
console.log(sum('10', '20'));

Flow只是一個小工具。

安裝:flow-bin

通過編譯除註解

  • 1.安裝flow-remove-types依賴
  • 2.使用JavaScript編譯工具babel配合flw外掛

flow開發者工具:flow language support

型別註解

function add(a:number,b:number){
	return a+b
}
let num:number = 90
function foo():void{}

flow原始型別

const a: string = 'foobar'
const b: number = Infinity // NaN // 100
const c: boolean = false // true
const d: null = null
const e: void = undefined
const f: symbol = Symbol()

flow陣列型別

const arr1: Array<number> = [1, 2, 3]
const arr2: number[] = [1, 2, 3]
// 元組
const foo: [string, number] = ['foo', 100]

flow物件型別

const obj1: { foo: string, bar: number } = { foo: 'string', bar: 100 }
const obj2: { foo?: string, bar: number } = { bar: 100 }
const obj3: { [string]: string } = {}
obj3.key1 = 'value1'
obj3.key2 = 'value2'

flow函數型別

 //@flow
function foo (callback: (string, number) => void) {
  callback('string', 100)
}
foo(function (str, n) {
  // str => string
  // n => number
})

flow特殊型別

// 字面量型別
const a: 'foo' = 'foo'
const type: 'success' | 'warning' | 'danger' = 'success'
// ------------------------
// 宣告型別
type StringOrNumber = string | number
const b: StringOrNumber = 'string' // 100
// ------------------------
// Maybe 型別
const gender: ?number = undefined

任意型別 Mixedany

//@flow
// string | number | boolean | ....
function passMixed (value: mixed) {
  if (typeof value === 'string') {
    value.substr(1)
  }
  if (typeof value === 'number') {
    value * value
  }
}
passMixed('string')
passMixed(100)
// ---------------------------------
function passAny (value: any) {
  value.substr(1)
  value * value
}
passAny('string')
passAny(100)

Typescript語言規範與基本應用

任何一種JavaScript執行環境都支援。功能更為強大,生態也更健全、更完善。

Typescript是JavaScript的超集。

微軟自研的開發工具對Typescript支援特別好。

Typescript(漸進式)–前端領域中的第二語言。

Typescript缺點:

  • 語言本身多了很多概念
  • 專案初期,Typescript會增加一些成本

使用Typescript之前要先安裝依賴。

標準庫就是內建物件所對應的宣告。

顯示中文錯誤資訊:yarn tsc --locale zh-CN,vs-code也可以設定locale為中文

Typescript作用域

我們可以用立即執行函數/export匯出模組來建立一個單獨作用域。

//立即執行函數
(function () {
    const a = 9
})()
//模組匯出
export aa = {
    a: 23
}

Typescript原始型別

//原始資料型別
const a: string = 'foo'
const b: number = 100  //NaN/Infinity
const c: boolean = true  //false
const d: boolean = null
const e: void = undefined
const f: null = null
const g: undefined = undefined
const h: symbol = Symbol()

Typescript Object型別

並不單指普通的物件型別,而是泛指所有的非原始型別:物件、陣列、函數。

//Object型別
const aa: object = function () { }  //[]//{}
const obj: { foo: number, bar: string } = { foo: 123, bar: 'aaa' }

更專業的方式是使用介面。

Typescript陣列型別

有兩種定義方式:

  • 使用Array泛型
  • 使用元素型別+[]
//陣列型別
const arr: Array<number> = [1, 2, 3]
const arr1: number[] = [1, 2, 3]

Typescript元組型別(turple)

是一種特殊的資料結構,其實元組就是一個明確元素數量以及每個元素型別的陣列,各個元素的型別不必要完全相同。定義方式:字面量方式

//元組型別
//元組(tuple)
export { }  //確保跟其他範例沒有成員衝突
const tuple: [number, string] = [10, 'rock']
console.log(tuple[0]);  //10
console.log(tuple[1]);  //rock
//解構賦值
const [num, age] = tuple
Object.entries({
    foo: 123,
    zar: 432
})

Typescript列舉型別(enum)

enum Status {
    Draft = 'aaa',
    Unpulished = 'bbb',
    Published = 'ccc'
}

如果確認程式碼中不會使用索引器去存取列舉,就可以使用常數列舉。

//常數列舉
const post = {
    title: 'Hello',
    content: 'TypeScript',
    status: 'ok'
}

列舉型別會入侵到我們執行時的程式碼,它會影響到我們編譯後的結果。我們在TypeScript中使用的大多數型別,它在經過編譯轉換後,最終都會被移除掉,因為它只是為了我們在編譯過程中做型別檢查,但列舉型別不會,它最終會變為一個雙向鍵值對物件。

TypeScript函數型別

JavaScript中又兩種函數定義方式:

函數宣告

//函數宣告方式
// function func1(a: number, b?: number): string {
// function func1(a: number, b: number=90): string {
function func1(a: number, ...rest: number[]): string {
    return 'hello'
}
func1(12, 34)
func1(30)

使用引數可選、引數預設值、剩餘引數都需要放在參數列的最後一個引數位置。

函數表示式

//函數表示式
const func2 = (a: number, b: number) => string = function (a: number, b: number): string {
    return 'func2'
}

TypeScript任意型別

因為any是弱型別,也是動態型別,所以TypeScript不會對any做型別檢查。所以它存在型別安全問題,我們不要輕易去使用它。

function stringify(value: any) {
    return JSON.stringify(value)
}
stringify('string')
stringify(123)
stringify(true)
let foo: any = 'string'
foo = 100
foo.bar()

隱式型別判斷

如果我們沒有通過型別註解去標註一個變數,TypeScript會根據這個變數的使用情況去推斷這個變數的型別。

//隱式型別推斷
let age = 10 //number
// age = 'aaa'
let foo;
foo = 45
foo = 'aaa'

雖然定義變數時如果不給它指定型別,也不給初始值,TypeScript會自動幫他註解為any型別,但還是建議定義時就註明型別。

TypeScript型別斷言

在某些特殊情況下,TypeScript無法去推斷一個變數的型別,而我們作為開發者,我們根據程式碼的使用情況,我們是會明確知道這個變數到底是什麼型別的。型別斷言的方式:

  • 使用as關鍵字
  • 在變數前面使用<>(JSX中不能使用)
const nums = [12, 34, 56]
const res = nums.find(i => i > 10)
//斷言方式一--as關鍵字
const num1 = res as number
//斷言方式二---泛型
const num2 = <number>res

型別斷言並不是型別轉換,型別轉換是程式碼在執行時的概念,而型別斷言是程式碼在編譯時的概念。當程式碼編譯過後,斷言也就不存在了。

TypeScript介面(Interface)

一種規範或者一種契約。它可以用來去約定物件的結構。去使用一個介面,就必須去遵守它所有的約定。

interface Post {
    title: string
    content: string
}
function printPost(post: Post) {
    console.log(post.title);
    console.log(post.content);
}
printPost({
    title: 'hello',
    content: 'welcome'
})
//hello
//welcome

TypeScript中的介面只是為了我們有結構的資料,去做型別約束的,在實際執行階段,它並沒有任何意義。

  • 可選成員
  • 唯讀成員
  • 動態成員
interface Post {
    title: string
    subtitle?: string   //可選成員
    content: string
    readonly summary: string  //唯讀成員
}

TypeScript類(class)

類的特徵:描述一類具體事物的抽象特徵。

類可以用來描述一類具體物件的抽象成員。

ES6以前,JavaScript都是通過函數+原型模擬實現類。ES6開始,JavaScript中有了專門的class。

TypeScript增強了class的相關語法。

class Person {
    name: string
    age: number
    constructor(name: string, age: number) {
        this.name = name
        this.age = age
    }
    sayHi(msg: string): void {
        console.log((`I am ${this.name}, ${msg}`));
    }
}

類的存取修飾符(預設是public修飾符)

class Person {
  public name: string // = 'init name'
  private age: number
  protected gender: boolean
  constructor (name: string, age: number) {
    this.name = name
    this.age = age
    this.gender = true
  }
  sayHi (msg: string): void {
    console.log(`I am ${this.name}, ${msg}`)
    console.log(this.age)
  }
}
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)
const jack = Student.create('jack', 18)

類的唯讀屬性readonly

protected readonly gender:boolean

類與介面

介面就是把共同的特徵封裝起來。

interface EatAndRun {
    eat(food: string): void
    run(distance: number): void
}
class Person implements EatAndRun {
    eat(food: string): void {
        console.log(`美美的吃:${food}`);
    }
    run(distance: number) {
        console.log(`直立行走:${distance}`);
    }
}
class Animal implements EatAndRun {
    eat(food: string): void {
        console.log(`美美的吃:${food}`);
    }
    run(distance: number) {
        console.log(`直立行走:${distance}`);
    }
}

一個介面只約束一個能力,一個型別去實現多個介面。

interface Eat {
    eat(food: string): void
}
interface Run {
    run(distance: number): void
}
class Person implements Eat, Run {
    eat(food: string): void {
        console.log(`美美的吃:${food}`);
    }
    run(distance: number) {
        console.log(`直立行走:${distance}`);
    }
}
class Animal implements Eat, Run {
    eat(food: string): void {
        console.log(`美美的吃:${food}`);
    }
    run(distance: number) {
        console.log(`直立行走:${distance}`);
    }
}

TypeScript抽象類

抽象類與介面有點類似,也可以用來去約束子類中必須擁有某個成員。

但是抽象類可以包含一些具體的實現,但是介面只是成員的抽象,不包含具體的實現。

抽象類不能用new去範例了,只能去繼承。

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(200)

TypeScript泛型

泛型:我們在定義函數、介面或類時,沒有指定具體的型別,等到使用時再去指定具體型別的特徵。

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 = CreateArray<string>(3, 'foo')

TypeScript型別宣告

一個成員在定義時沒有宣告型別,在使用時單獨為它做出明確宣告。

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')

以上就是Typescript型別系統FLOW靜態檢查基本規範的詳細內容,更多關於Typescript型別FLOW靜態檢查的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com