TypeScript

类型系统

强类型与弱类型(类型安全)

强类型

​ 语言层面限制函数的实参类型必须与形参类型相同

//TypeScript
class Main {
    static void foo(int num) {
        System.out.println(num)
    }

    public static void main(String[] args) {
        Main.foo(100); //ok
        Main.foo("100"); //error "100" is a string
        Main.foo(Integer.parseInt("100")); //ok
    }
}

弱类型

​ 弱类型语言层面不会限制实参的类型

//JavaScript
function foo(num) {
    console.log(num);
}

foo(100) //ok
foo("100") //ok
foo(parseInt("100")) //ok

区别:

  1. 强类型有更强的类型约束,而弱类型中几乎没有什么约束
  2. 强类型语言中不允许任意的隐式类型转换,而弱类型语言是允许的
静态语言与动态语言(类型检查)

静态类型语言

一个变量声明时它的类型就是明确的,声明过后它的类型就不允许再修改

动态类型语言

运行阶段才能够明确变量类型,而且变量的类型随时可以改变。也可以理解为动态类型语言中的变量没有类型,变量中存放的值是有类型的。

强类型语言优势

  1. 错误更早暴露
  2. 代码更智能,编码更准确
  3. 重构更牢靠
  4. 减少不必要的类型判断
Flow

JavaScript的类型检查工具,通过为参数添加类型注解,去标识参数的类型。通过flow或babel能够解析类型注解,将其转化为普通JavaScript代码。

// Flow
function sum (a:number,b:number){
	return a+b
}
function sum (a:number,b){
	// a : number
	// b : any
	return a+b
}
Flow安装与使用
yarn

1、安装flow

yarn add flow-bin --dev

2、初始化flow,生成 .flowconfig 配置文件

yarn flow init

3、项目中使用

//@flow
// 为参数添加类型注解,以标识参数类型。须在文件开头以注释的方式添加@flow标记,这样flow才会对文件进行类型检查。
function sum(a:number, b:number) {
    return a + b;
}

sum(100,100)
sum('100','100')

4、运行flow&&关闭flow

yarn flow
yarn flow stop

5、通过编译移除类型注解(类型注解非JavaScript标准语法,无法正常运行)

  1. flow-remove-types

    安装

    yarn add flow-remove-types --dev
    

    使用 yarn flow-remove-types <源代码目录> -d <输出目录>

    yarn flow-remove-types . -d dist
    
  2. babel

    安装

    //安装babel preset-flow 
    yarn add --dev @babel/core @babel/cli @babel/preset-flow
    

    配置

    //将flow添加到Babel预设配置中(在根目录创建.babelrc文件,添加如下设置)
    {
      "presets": ["@babel/preset-flow"]
    }
    

    使用 yarn babel <源代码目录> -d <输出目录>

    yarn babel src -d -dist
    
npm

1、安装flow

npm init -y
npm i flow-bin -D

2、在package.json中添加执行指令

  "scripts": {
    "flow": "flow",
  },

3、初始化flow,生成 .flowconfig 配置文件

npm run flow init

4、项目中使用

//@flow
// 为参数添加类型注解,以标识参数类型。须在文件开头以注释的方式添加@flow标记,这样flow才会对文件进行类型检查。
function sum(a:number, b:number) {
    return a + b;
}

sum(100,100)
sum('100','100')

5、运行flow&&关闭flow

npm run flow
npm run flow stop

6、通过编译移除类型注解(类型注解非JavaScript标准语法,无法正常运行)

  1. flow-remove-types

    安装

    npm install --save-dev flow-remove-types
    

    配置 flow-remove-types <源代码目录> -d <输出目录>

    //package.json中修改配置
    # 把目录文件编译之后转到dist目录下
    

“scripts”: {
“flow”: “flow”,
“flowRemove”: “flow-remove-types src -d dist/”
}


 **使用**

npm run flowRemove


2. babel

**安装**

//安装babel preset-flow
npm install --save-dev @babel/core @babel/cli @babel/preset-flow


**配置**

//Babel配置
//将flow添加到Babel预设配置中(在根目录创建.babelrc文件,添加如下设置)
{
“presets”: [“@babel/preset-flow”]
}

//package.json文件配置
“scripts”: {
“build”: “babel ./src -d ./dist”
}


**使用**

npm run build


###### 开发工具插件

**Flow Language Support** 

​		修改后再次保存才会校验,会有延迟

##### Flow基本使用

###### 类型推断

/**

  • 类型推断
  • @flow
    /
    function square(num){
    return num
    num;
    }
    // square(‘10’) //报错,自动推断num应为number

###### 类型注解

/**

  • 类型注解
  • @flow
    /
    // 参数类型注解
    function square(num:number){
    return num
    num;
    }

// 变量类型注解
let num:number=123
// num=‘123’

// 函数返回值类型注解
function foo():number{
// return ‘abc’
return 123
}


###### 原始类型

/**

  • 原始类型
  • @flow
    */
    const a: string = ‘foobar’

const b: number = Infinity //NaN //100

const c: boolean = true //false

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] //数字数组

//元组 固定长度的数组
//两个元素的数组,第一个元素为string类型,第二个元素为number类型
const arr3: [string, number] = [‘str’, 2]


###### 对象类型

/**

  • 对象类型
  • @flow
    */

// foo为string类型, bar为number类型
const obj1: { foo: string, bar: number } = { foo: ‘abc’, bar: 123 }

// foo设为可选
const obj2: { foo?: string, bar: number } = { bar: 123 }

// 键为string类型,值为number类型
const obj3: { [string]: number } = {}
obj3.key1=123
obj3.key2=456


###### 函数类型

/**

  • 函数类型
  • @flow
    */

// 指定callback为函数类型,且第一个参数为string类型,第二个参数为number类型,返回值为boolean类型
function foo(callback: (string, number) => boolean) {
callback(‘str’, 100)
}
foo(function (str, n) {
// str => string
// n => number
return n > 0
})


###### 特殊类型

/**

  • 特殊类型
  • @flow
    */
    // a中只能存放’foo’字符串
    const a: ‘foo’ = ‘foo’

// type中只能存放’success’/‘warning’/'danger’其中一个
const type: ‘success’ | ‘warning’ | ‘danger’ = ‘success’

// b为string或number类型
const b: string | number = “100”

// 使用type给一个类型组合取别名
type StringOrNumber = string | number
const c: StringOrNumber = 100

// maybe类型 gender为number或undefined或null
const gender: ? number = undefined
// 相当于
const gender1: number | null | void = undefined


###### Mixed&&Any类型

/**

  • Mixed Any
  • @flow
    */
    // 强类型,类型安全 value为一个具体的类型
    // value可以为任意类型,string|number|boolean|…
    function passMixed(value: mixed) {
    if (typeof value === ‘string’) {
    value.substr(1)
    }
    if (typeof value === ‘number’) {
    value * value
    }
    // value.substr(1) //直接使用会报错
    // value * value
    }
    passMixed(123)
    passMixed(‘string’)

// 弱类型
// value可以为任意类型,string|number|boolean|…
function passAny(value: any) {
value.substr(1)
value * value
}
passAny(123)
passAny(‘string’)


### TypeScript

TypeScript 是 JavaScript 的一个超集,支持 ECMAScript 6 标准。

TypeScript 设计目标是开发大型应用,它可以编译成纯 JavaScript,编译出来的 JavaScript 可以运行在任何浏览器上。

#### 安装配置与使用

##### 安装

npm install -g typescript


##### 编译单个文件

将单个TS文件编译成JS文件

tsc greeter.ts


##### 配置文件

使用如下命令,将会在项目根目录下生成tsconfig.json文件

tsc --init


**常用选项:**

- target

  指定编译后的JavaScript版本(ES5、es2015、es2016...)

“target”:“es2016”


- module

指定编译后模块化的语法。可选择 none、commonjs、amd、system、umd、es2015或esnext

“module”:“commonjs”


- rootDir

指定源代码目录

“rootDir”:“src”


- outDir

指定编译后生成文件的输出目录

“outDir”:“dist”


- sourceMap

是否为生成的JS文件创建源映射文件

此文件允许调试器和其他工具在实际使用发出的JavaScript文件时显示原始的TypeScript源代码。此文件为 .js.map (or .jsx.map) 格式,位于相应的.js 输出文件相同目录

“sourceMap”:true


- strict

是否开启严格模式

“strict”:true


##### 使用配置文件

将整个项目下的TS文件,编译为JS文件

//直接在项目根路径使用tsc命令
tsc


#### TS数据类型

##### 原始数据类型

/**

  • 原始数据类型
  • 与Flow中的差异:
  •  非严格模式下,变量可以为空(null undefined)
    

*/
let a:string=‘abc’ //string
// 非严格模式下(“strict”: false),a可以为null或undefined
a=null
a=undefined

let b:number=123 //number Infinity NaN

let c:boolean=true //boolean

let d:null=null //null

let e:void=undefined //undefined

let f:symbol=Symbol() //ES6新增特性,ES6以下版本(“target”:“es5及以下”)需在标准库中添加对应声明文件(“lib”:[“es2015”])

let g:undefined=undefined //undefined


##### 标准库声明

标准库就是内置对象(Symbol、Promise、BOM、DOM...)所对应的声明

“lib”:[“ES2015”,“DOM”]


##### Object类型

/**

  • object类型
    */

// 可以接收函数、数组、对象等其他非原始类型
const foo: object = function () { }//[]//{}

// 只接收对象,字面量方式,左右结构必须一致
const obj: { foo: string } = { foo: ‘123’ }

export { }


##### 数组类型

/**

  • array类型
    */

const arr1: Array = [1, 2, 3]

const arr2: string[] = [‘1’, ‘2’, ‘3’]

function sum(…args: number[]) {
// 将参数定义为数字数组,累加前不用再判断传入参数是否为数字
return args.reduce((prev, cur) => prev + cur, 0)
}

sum(1,2,3)


##### 元组类型

明确元素数量和每个元素类型的数组

/**

  • 元组类型(Tuple)
    */
    // 明确元素个数和类型
    const tuple: [number, string] = [123, ‘abc’]

export {}


##### 枚举类型

/**

  • 枚举类型(Enum)
    /
    // 模拟枚举类型
    /
    const PostStatus = {
    Draft: 0,
    Unpublished: 1,
    Published: 2
    } */

//声明一个枚举类型
/* enum PostStatus {
Draft = 0,
Unpublished = 1,
Published = 2,
} */

//不给枚举类型赋值,默认从0开始累加,若给某个成员赋值,下一个成员将在其基础上累加
/* enum PostStatus {
Draft, //0
Unpublished=3, //3
Published, //4
} */

//若枚举成员值为字符串,则需要给每个成员赋值,无法累加
/* enum PostStatus {
Draft = ‘aaa’,
Unpublished = ‘bbb’,
Published = ‘ccc’,
} */

// 枚举类型会入侵代码,编译过后会生成一个双向的键值对对象,可通过值去获取键,也能通过键获取值
/* PostStatus[‘Draft’] //0
PostStatus[0] //Draft */

// 若无需通过索引器去访问枚举类型,可使用常量枚举,常量枚举编译过后会被移除
const enum PostStatus {
Draft,
Unpublished,
Published
}

// status的值为 0 草稿 1 未发布 2 已发布
const post = {
title: ‘Hello TypeScript’,
content: ‘TypeScript is a typed superset of JavaScript’,
// status: 0,//1 //2
status: PostStatus.Draft
}

export { }


##### 函数类型

/**

  • 函数类型
    */
    // 为函数参数和返回值指定数据类型
    function fun1(a: number, b: string): void {
    b = ‘QvQ’ + a;
    }
    fun1(100, ‘abc’) //参数个数和数据类型必须与定义的一致
    // fun1(100) //报错
    // fun1(‘abc’) //报错

//若希望将参数设置为可选参数,可使用 可选运算符? 或 使用参数默认值 的方式
function fun2(a?: number, b: string = ‘aaa’, …args: number[]): void {
b = ‘QvQ’ + a;
}
fun2()

// 使用函数表达式的方式声明函数
//func 接收一个函数类型的值,该函数第一个参数为number类型,第二个参数为string类型,且函数返回值为string类型
const func: (a: number, b: string) => string = function (a: number, b: string): string {
return a + ‘-’ + b
}
export { }


##### 任意类型

/**

  • any任意类型(弱类型)
  • any类型是不安全的
    */
    // value参数可以接收任意类型的值
    function stringify(value: any) {
    return JSON.stringify(value)
    }
    stringify(123)
    stringify(‘abc’)
    stringify(true)

// any类型为动态类型,可以接收任意类型的值
let foo: any = 100
foo = ‘abc’
foo = false

export { }


##### 隐式类型推断

/**

  • 隐式类型推断
    */
    let num = 18 //此处num被推断为number类型
    // num = ‘abc’ //赋值string类型的值将会报错

let foo //未指定类型且未赋值,将会推断为any类型,此时foo可以接收任意类型的值
foo = 123
foo = ‘abc’

// 为方便理解代码,建议为每个变量添加明确的类型


##### 类型断言

/**

  • 类型断言
    /
    const nums = [1, 2, 3, 4]
    const res = nums.find(item => item > 0) //此时res类型为number|undefined
    // const square=res
    res //此时因为res类型可能为undefined类型,无法进行运算,所以会报错

let res1 = res as number //使用as关键字将res类型断言为number类型,类型断言不是类型转换,只是在编译过程将其明确为某种类型
let res2 = res //使用尖括号的方式断言,JSX下会与其语法冲突,不能使用
const square1 = res1 * res1 //不会报错
const square2 = res2 * res2 //不会报错

export { }


#### TS接口

###### 接口(interface)

用来约束对象的结构,一个对象去实现一个接口,就必须拥有这个接口所定义的所有成员。

/**

  • 接口
  • 用来约束对象的结构
    */
    // 定义一个接口
    interface News {
    title: string
    content: string
    }
    // news参数必须拥有title和content属性,此时可以定义一个接口
    function showNews(news: News) {
    console.log(news.title);
    console.log(news.content);
    }
    showNews({
    title: ‘Good Night!’,
    content: ‘Bye~’
    })

export{}


###### 可选、只读、动态成员接口

/**

  • 接口
  • 可选成员、只读成员、动态成员
    */
    // 定义一个接口
    interface News {
    title: string
    content: string
    subtitle?: string //可选成员
    readonly summary: string //只读成员 实现接口后summary值不可再修改
    }
    const news1: News = {
    title: ‘Good Night!’,
    content: ‘Bye~’,
    summary: ‘2022’
    }
    // news1.summary=‘hello’ //只读属性,无法修改,报错

// 动态成员接口 不明确具体属性
interface DynamicInt {
[key: string]: number
}
const dynamic1: DynamicInt = {
width: 12,
height: 20,
cycle: 64
}
export { }


#### TS类

用来描述一类具体对象的抽象成员。ES6以前,使用函数+原型模拟实现类,ES6开始JavaScript中有了专门的class。TypeScript增强了class的相关语法

##### 基本使用

/**

  • 类的基本使用
    */
    class Person {
    name: string //= ‘init name’
    age: number

    constructor(name: string, age: number) {
    //TS中类的属性必须先定义,不能直接在构造函数中添加(为了给属性指定类型)
    //属性必须在定义时赋初始值或在构造函数中初始化
    this.name = name
    this.age = age
    }

    say(msg: string): void {
    console.log(${msg},${this.name});
    }
    }

export { }


##### 类的访问修饰符

/**

  • 类的访问修饰符 默认为public

  • public 公有的 都能访问

  • private 私有的 只有类内部能够访问

  • protected 受保护的 只有类的内部和子类中可以访问
    */
    class Person {
    public name: string
    private age: number
    protected gender: boolean

    constructor(name: string, age: number, gender: boolean) {
    this.name = name
    this.age = age
    this.gender = gender
    }

    say(msg: string): void {
    console.log(${msg},${this.name});
    }
    }
    const p1 = new Person(‘xiaoming’, 23, true)
    console.log(p1.name); //可以访问
    // console.log(p1.age); //私有属性,外部不能访问
    // console.log(p1.gender); //受保护的属性,只能在类“Person”及其子类中访问。

class Student extends Person {
public stuNumber: string
// 构造函数也可以添加访问修饰符
private constructor(name: string, age: number, gender: boolean, stuNumber: string) {
super(name, age, gender)
this.stuNumber = stuNumber
// console.log(this.age); //子类中不能访问父类中的private属性
console.log(this.gender); //子类中可以访问父类中的protected属性
}

static createStu(name: string, age: number, gender: boolean, stuNumber: string): Student {
    return new Student(name, age, gender, stuNumber)
}

}
const s1 = Student.createStu(‘xiaowang’, 20, false, ‘20230101’)

export { }


##### 类的只读属性

/**

  • 类的只读属性 readonly
  • 只读属性,在初始化后不允许再次修改
  • 若存在访问修饰符,readonly应放在访问修饰符后面
    */

class Person {
public name: string
public readonly sex: string

constructor(name: string, sex: string) {
    this.name = name
    this.sex = sex
}

}
const p1=new Person(‘xigua’,‘M’)
p1.name=‘xiaoming’
// p1.sex=‘F’ //只读属性,初始化后不允许再次修改

export {}


##### 类与接口

使用interface定义接口,使用implements实现接口,一个类可以实现多个接口,接口名之间以逗号分隔。

/**

  • 类与接口
    */
    // 定义接口
    interface EatAndSay {
    eat(food: string): void
    say(word: string): void
    }

interface Eat {
eat(food: string): void
}
interface Say {
say(word: string): void
}
// 使用implements实现接口
class Person implements EatAndSay {
eat(food: string) {
console.log(‘用餐:’, food);
}
say(word: string) {
console.log(‘hello’, word);
}
}

// 实现多个接口,用逗号分隔
class Animal implements Eat, Say {
eat(food: string) {
console.log(‘进食:’, food);
}
say(word: string) {
console.log(‘汪汪汪?’, word);
}
}
export { }


##### 抽象类

使用abstract关键字声明当前类为抽象类,抽象类不能使用new生成实例,只能用来继承。抽象类中可以实现具体的方法继承给子类,也可以使用abstract关键字声明抽象方法等待子类实现。

/**

  • 抽象类
    */
    // 声明一个抽象类
    abstract class Animal {
    eat(food: string) {
    console.log(‘进食:’, food);
    }
    // 声明一个抽象方法
    abstract run(distance: number): void
    }
    // 继承一个抽象类
    class Dog extends Animal {
    run(distance: number): void {
    console.log(‘爬行:’, distance);
    }
    }
    const dog1 = new Dog()
    dog1.eat(‘鸡腿’)
    dog1.run(4)

// ========================
// 定义一个接口
interface Run {
run(speed: number): void
}
// 声明一个抽象类
abstract class Worker {
sleep(time: Object): void {
console.log(‘sleeping…’);
}
abstract work(skill: string): string[]
}
// 尝试new一个抽象类
// const w1=new Worker() //无法创建抽象类的实例,报错
// 继承一个抽象类并实现一个接口
class Programmer extends Worker implements Run {
constructor() {
super()
}
work(skill: string): string[] {
return Array(5).fill(skill)
}
run(speed: number) {
console.log(‘run’, speed);
}
}
const p1 = new Programmer()
p1.work(‘CV’)
p1.sleep(8)

export { }


#### 泛型

通过在函数名后使用<T>声明一个泛型T,代表传入参数的类型,后续传入的参数为什么类型,T就为什么类型

/**

  • 泛型
    */
    // 实现一个能生成数字数组的函数
    function createNumberArr(length: number, value: number): number[] {
    const arr = Array(length).fill(value)
    return arr
    }
    // 实现一个能生成字符串数组的函数
    function createStringArr(length: number, value: string): string[] {
    const arr = Array(length).fill(value)
    return arr
    }
    console.log(createNumberArr(5, 333));
    console.log(createStringArr(5, ‘abc’));

// 使用泛型实现一个能够创建任意类型数组的函数 使用声明一个泛型T,指代后续传入的类型
function createArr(length: number, value: T): T[] {
const arr = Array(length).fill(value)
return arr
}
console.log(createArr(5, 333));
console.log(createArr(5, ‘abc’));

export { }


#### 类型声明

使用declare关键字可以为引入的第三方库,提供类型声明

/**

  • 类型声明
    */
    import { camelCase } from ‘lodash’

// 进行类型声明
// declare function camelCase(str: string): string

const str = camelCase(‘hello typescript’)

export { }


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值