- 強類型與弱類型(類型安全)
(此處存在爭議,以下觀點僅代表個人理解) 編程語言分爲強類型與弱類型,強類型有更強的類型約束,而弱類型中幾乎沒有什麼約束
- 強類型語言
- 在語言層面就限制了函數的實參類型必須與形參類型相同
- 不允許任意數據的隱式轉換
- 錯誤更早暴露
- 代碼更智能,編碼更準確
- 重構更加牢靠
- 減少不必要的類型判斷
- 弱類型語言
- 在語言層面不會限制實參的類型
- 語序任意數據的隱式轉換
- 強類型語言
- 靜態類型與動態類型(類型檢查)
- 靜態類型語言
- 一個變量聲明時它的類型就是明確的
- 聲明後它的類型不允許在修改
- 編譯是需要做類型檢查
- 動態類型語言
- 聲明的變量是沒有類型的
- 變量中存放對的值是有類型的
- 靜態類型語言
- JavaScript自有類型系統特徵
- 弱類型且動態類型
- 靈活多變
- 可靠性低
- 沒有編譯環節
// 程序類型異常,必須等待執行時纔會報錯
const obj = {}
obj.foo()
// 兩個數組相加,可能會傳入非Number參數
funciton sum (a,d){
return a + b
}
sum(100,'abc')
//可以使用任意類型作爲屬性 屬性名會自動通過toString轉換
const obj = {}
obj[true] = 100
console.log(obj['true']) // 100
-
Flow靜態類型檢查器
2015年由FaceBook提供的一款工具,爲JavaScript提供完善的類型檢查方案
-
工作原理
在代碼中通過添加類型註解的方式來標記每一個變量或者參數的類型,flow 根據註解解析類型,從而發現類型錯誤。
-
安裝
# 安裝 yarn add flow-bin --dev # 初始化 yarn flow init
-
新建一個js文件且在最上部通過註釋形式寫 //@flow
// @flow function sum (a:number,b:number){ return a + b } sum(1,2) sum('a','b')
-
執行 yarn flow
-
flow編譯移除註解–flow-remove-types
# 安裝flow-remove-types
yarn add flow-remove-types --dev
# 執行 flow-remove-types
# . 當前目錄
# -d 代表 --out-dir
# dist 輸出目錄
yarn flow-remove-types . -d dist
-
編譯後的文件
-
flow編譯移除註解–babel
# 安裝 @babel/core babel核心模塊
yarn add @babel/core --dev
# 安裝 @babel/cli babel命令行工具
yarn add @babel/cli --dev
# 安裝 @babel/preset-flow babel Flow註解插件
yarn add @babel/preset-flow --dev
- 新建 .babelrc
{
"presets" : [
"@babel/preset-flow"
]
}
# 執行
# src 工作目錄
# -d --out-dir 輸出
# dist 輸出文件夾
yarn babel src -d dist
-
flow + vscode+Flow Language Support 食用更加
-
flow 類型註解
@flow
// 形參類型註解
function sum (a:number,b:number){
return a + b
}
// 變量類型註解
const num:number = 100
// 返回值類型註解
function foo():number{
return 100
}
// 返回值爲空的類型註解
function baz():void{
return 100
}
- flow 原始數據類型註解
const a : string = 'string'
const b : number = Infinity // NaN Infinity 100
const c : boolean = true // true false
const d : null = null
const e : void = undefined
const f : symbol = Symbol()
// 數組數據類型限制
const arr1: Array < string > = ['a', 'b', 'c']
const arr2: number[] = [1, 2, 3, 4, ]
// 固定長度的數組也可以成爲元組數據類型
const arr3: [String, number] = ['a', 100]
// 對象數據類型限制
const obj: {
foo: string,
bar: number
} = {
foo: 'string',
bar: 100
}
// 可選成員
const obj2: {
foo: string,
bar ? : number
} = {
foo: 'string',
bar: 100
}
// 對象允許添加任意類型 但是 鍵只可以爲 string 值只能是 number
const obj3: {
[string]: [number]
} = {}
obj3.boo = 100
obj3.baz = 200
// 函數數據類型限制
function foo({
num: number
}): number {
return 100
}
// 設置回調函數據類型限制
function foo(callback: (number) => number): number {
return callback()
}
// 特殊數據類型
// 字面量類型
const a: 'foo' = 'foo' //只能存放 foo字符串
// 聯合變量
const type: 'success' | 'warning' | 'danger' = 'success' //只能存放 'success'|'warning'|'danger'
const b: string | number = 'string' //100
// 類型別名 or 用 type 聲明一個類型
const StringOrNumber = string | number
const c : StringOrNumber = 'string' //100
// mybe類型
const gender : ?number = null
// Mixed 數據類型 強類型 所有類型的集合
function passMixed(value:Mixed){
if(typeof value === 'string'){
value.substr(1)
}
if(typeof value === 'number'){
value * value
}
}
passMixed(100)
passMixed('string')
// any數據類型 弱類型
function passAny(value:Any){
value.substr(1)
value * value
}
passAny(100)
passAny('string')
// 運行環境內置限制
const element: HTMLElement | null = document.getElementById('abc')
- TypeScript語言規範與基本應用
TypeScript是JavaScript超集,編譯過爲JavaScript
# 初始話工程
yarn
# 安裝TypeScript
yarn add typescript --dev
- 簡單案例
const fn = (str: string) => {
console.log(`hello ${str}`)
}
fn('word')
# 編譯
yarn tsc 01-test.ts
編譯過後可以在當前目錄下 看到同名成的.js文件
var fn = function (str) {
console.log("hello " + str);
};
fn('word');
- TypeScript 配置文件
# 生成配置文件
yarn tsc --init
# 執行之後會新增tsconfig.json
- tsconfig.json 常用配置註解
{
"compilerOptions": {
"target": "ES5", /* 編譯後輸出版本: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
"module": "commonjs", /* 編譯後輸出js模塊化加載方式: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
"outDir": "./dist", /* 輸出路徑.*/
"rootDir": "./src", /* 源代碼所在路徑 */
"sourceMap": true, /* 開啓源代碼映射 */
"strict": true, /* 嚴格模式 */
"esModuleInterop": true, /* Enables emit interoperability between*、
}
}
- 目錄結構
├── dist
│ ├── 01-test.js
│ └── 01-test.js.map
├── node_modules
├── package.json
├── src
│ └── 01-test.ts
├── tsconfig.json
└── yarn.lock
- 執行編譯
tsc
- TypeScript原始類型
const a: string = 'string'
const b: number = Infinity // 100 NaN
const c: boolean = true
const d: void = undefined
const e: null = null
const f: undefined = undefined
const g: symbol = Symbol()
// 對象類型 支持數組 對象 函數
const foo: object = {} // [] funciton(){}
const obj: { foo: number, bar: string } = { foo: 124, bar: 'string' }
// 數組類型
const arr1: Array<number> = [1, 2, 4]
const arr2: number[] = [1, 2, 4]
// 元組類型
const tupple: [number, string] = [18, 'string']
// 枚舉類型
enum PostStatus {
Padding = 0,
Success = 1,
Fail = 2
}
// 如果不賦值那麼會自動的從0開始累加
enum PostStatus2 {
Padding,
Success,
Fail
}
console.log(PostStatus2['Success']) // 1
console.log(PostStatus['Success']) // 1
// 函數類型
// ? 代表可選參數
function func1(a:string, b:number,c?:string):string{
return `func1 a=${a},b=${b},c=${c}`
}
console.log(func1('a',1) ) // func1 a=a,b=1,c=undefined
console.log(func1('a',1,'c')) // func1 a=a,b=1,c=c
// 通過 = 給與默認值
function func2(a:string, b:number,c:string='默認值'):string{
return `func1 a=${a},b=${b},c=${c}`
}
console.log(func2('a',1) ) // func1 a=a,b=1,c=默認值
console.log(func2('a',1,'c')) // func1 a=a,b=1,c=c
// 箭頭函數方式
const func3 :(a:string, b:number,c?:string)=>string = (a:string, b:number,c:string='默認值')=>{
return `func1 a=${a},b=${b},c=${c}`
}
console.log(func3('a',1) ) // func1 a=a,b=1,c=默認值
console.log(func3('a',1,'c')) // func1 a=a,b=1,c=c
// 使用 ...解構並檢查數據類型
function sum(...args: number[]) {
return args.reduce((prev, current) => prev + current, 0)
}
sum(1, 2, 3)
// 任意類型參數 any 屬於動態類型 ts不會檢查
function stringify(value:any):string{
return JSON.stringify(value)
}
- TypeScript 隱式類型推斷
雖然通過隱式類型推斷可以簡化一部分代碼,但是會降低閱讀性。所以不建議通過隱式類型推斷出變量類型
const age = 18 //推斷爲 number
const name = 'zzy' //推斷爲 string
const foo //推斷爲 any
// ...
- TypeScript 類型斷言
const nums = [1, 2, 3, 4, 5]
const res = nums.find(i => i > 0) // 推斷爲 number | undefined
const num1 = res as number // 斷言 res的類型一定爲 number
const num2 = <number>res // 斷言 res的類型一定爲 number(尖括號模式在jsx會有語法衝突)
- TypeScript interface 接口(規範/契約)
interface Post {
title:string
content:string
}
function pringPost(post:Post){
console.log(post.title)
console.log(post.content)
}
pringPost({
title:'zzy',
content:'hello word'
})
- TypeScript interface 只讀屬性,可選屬性
interface Post {
title:string
content:string
// 可選成員
desc?:string
// 只讀屬性
readonly sunmary:string
}
const post1:Post = {
title:'Title is post1',
content:'content is post1',
sunmary:'sunmary is post1'
}
// 修改普通屬性
post1.title = 'update post1 title'
// 嘗試修改只讀屬性
//post1.sunmary='str' // 報錯 Cannot assign to 'sunmary' because it is a read-only property.
- TypeScript interface 動態添加成員
interface Cache {
// 可以動態添加成員
[porp:string]:string|number|number[]
}
const obj:Cache = {}
obj.test1='abc'
obj.test2=1234
obj.test3=[1,2,3,4,5]
- TypeScript interface 接口繼承
interface Person{
name:string
age:number
}
interface Student extends Person{
classesId:number
studentId:number
}
const student:Student = {
classesId:1,
studentId:2,
name:'student1',
age:18
}
- TypeScript 類
類的作用描述一類具體事物的抽象特徵
- TypeScript 類 基本屬性
class Person {
name:string
age:number
status:boolean=true
constructor (name:string,age:number){
this.name=name
this.age=age
}
sayHi(msg:string):void{
console.log(`${this.name} say ${msg}`)
}
}
- TypeScript 類 修飾符
控制類中的成員可訪問級別
- public 公共屬性
- private 私有屬性 只允許在當前類中訪問的成員
- protected 受保護的 允許在子類或當前類中訪問的成員
class Person {
// public 公共屬性
public name:string
// private 私有屬性 只允許在當前類中訪問的成員
private age:number
// protected 受保護的 允許在子類或當前類中訪問的成員
protected status:boolean=true
constructor (name:string,age:number){
this.name=name
this.age=age
}
sayHi(msg:string):void{
console.log(`${this.name} status is ${this.status}`)
console.log(`${this.name} age is ${this.age}`)
console.log(`${this.name} say ${msg}`)
}
}
class Student extends Person{
constructor(name:string,age:number){
super(name,age)
console.log(this.name)
//console.log(this.age) // 報錯 Property 'age' is private and only accessible within class 'Person'.
console.log(this.status)
}
}
const tom = new Student('tom',18)
- TypeScript 類 constructor與修飾符
private修飾constructor,則constructor只能通過類上的static方法創建
class Person {
public name:string
private age:number
private constructor (name:string,age:number){
this.name=name
this.age=age
}
sayHi(msg:string):void{
console.log(`${this.name} age is ${this.age}`)
console.log(`${this.name} say ${msg}`)
}
static create(name:string,age:number):Person{
return new Person(name,age)
}
}
const person = Person.create('tom',18)
person.sayHi('hello everybody')
// tom age is 18
// tom say hello everybody
privateed修飾constructor,則constructor可以通過類上的static方法創建及繼承
class Person {
public name:string
private age:number
protected constructor (name:string,age:number){
this.name=name
this.age=age
}
sayHi(msg:string):void{
console.log(`${this.name} age is ${this.age}`)
console.log(`${this.name} say ${msg}`)
}
static create(name:string,age:number):Person{
return new Person(name,age)
}
}
const person = Person.create('tom',18)
person.sayHi('hello everybody')
// tom age is 18
// tom say hello everybody
class Student extends Person{
constructor(name:string,age:number){
super(name,age)
console.log(this.name)
}
}
const tom = new Student('tom',18)
tom.sayHi('hello everybody')
// tom age is 18
// tom say hello everybody
- TypeScript 類 readonly 只讀屬性
class Person {
public name:string
private readonly age:number // readonly 一般跟在修飾符後面
protected constructor (name:string,age:number){
this.name=name
this.age=age
}
sayHi(msg:string):void{
//this.age=20 // 修改只讀屬性報錯 Cannot assign to 'age' because it is a read-only property.
console.log(`${this.name} age is ${this.age}`)
console.log(`${this.name} say ${msg}`)
}
static create(name:string,age:number):Person{
return new Person(name,age)
}
}
const person = Person.create('tom',18)
person.sayHi('hello everybody')
// tom age is 18
// tom say hello everybody
- TypeScript 類與接口
通過接口定義類的成員
interface EatAndRun {
eat(food: string): void
walk(step: number): void
}
class Person implements EatAndRun{
eat(food: string): void {
console.log(`優雅進餐${food}`)
}
walk(step: number): void {
console.log(`直立行走${step}`)
}
}
class Animal implements EatAndRun{
eat(food: string): void {
console.log(`粗魯進食${food}`)
}
walk(step: number): void {
console.log(`爬行${step}`)
}
}
接口最好的使用或者定義方式:一個接口對應一個能力
interface Eat {
eat(food: string): void
}
interface walk {
walk(step: number): void
}
class Person implements Eat,walk{
eat(food: string): void {
console.log(`優雅進餐${food}`)
}
walk(step: number): void {
console.log(`直立行走${step}`)
}
}
class Animal implements Eat,walk{
eat(food: string): void {
console.log(`粗魯進食${food}`)
}
walk(step: number): void {
console.log(`爬行${step}`)
}
}
- TypeScript 類抽象類
abstract class Animal{
eat(food: string): void {
console.log(`粗魯進食${food}`)
}
// 定義行走抽象方法
abstract walk (step: number): void
}
class Dog extends Animal{
// 實現行走放大
walk(step: number): void {
console.log(`爬行${step}`)
}
}
const dog = new Dog
console.log(dog.eat('骨頭')) // 粗魯進食骨頭
console.log(dog.walk(10)) // 爬行10
- TypeScript 泛型
定義函數接口或者類的時候不能明確的類型,在使用的時候在指定具體類型
// 只能實現 返回 number 格式的數組
function createNumberArray(len:number,value:number):number[]{
const arr=Array<number>(len).fill(value)
return arr
}
console.log(createNumberArray(3,100)) // [ 100, 100, 100 ]
// 不明確的類型使用 T 來表示 T 會在函數調用時傳入
function createArray<T>(len:number,value:T):T[]{
const arr=Array<T>(len).fill(value)
return arr
}
console.log(createArray<number>(3,100)) // [ 100, 100, 100 ]
console.log(createArray<string>(3,'abc')) // [ 'abc', 'abc', 'abc' ]