TypeScript知识点
强类型与弱类型(类型安全)
强类型
语言层面限制函数的实参类型必须与形参类型相同。可以理解为强类型语言中不允许有任意的数据隐式类型转换。
弱类型
弱类型语言层面不会限制实参的类型。可以理解为弱类型语言中允许任意的数据隐式类型转换。
静态类型与动态类型(类型检查)
静态类型
一个变量在声明时类型就是明确的,声明过后,它的类型就不允许被修改
动态类型
运行阶段才能确定变量的类型,并且变量的类型随时可以改变。
JavaScript自有类型系统的问题
JavaScript是动态类型的弱类型语言,JavaScript没有编译环节。
弱类型带来的问题
1.程序运行时的类型异常需要等到执行的时候才能发现,如:
const obj = {};
obj.foo(); // 运行时才会抛出错误
2。类型不明确造成函数功能不是原本想要实现的功能。 如:
function sum(a, b){
return a + b;
}
sum(100, 100); // 正常程序需要的结果
sum(100, '100'); // 不是自己想要的方法
3.对对象索引器错误的用法。如:
const obj = {};
obj[true] = 100;
console.log(obj['true']);
强类型的优势
1.错误更早暴露,不会像弱类型一样等到运行时才报错。
2.代码更智能,编码更准确。就好比智能提示可以根据你的输入进行准确的判断,而不会出现像弱类型无法根据类型进行智能提示。
3.重构更牢靠,例如定义了一个属性或方法,在后续想要进行更改的时候,弱类型语言无法及时体现出错误,对重构的速度影响很大。
4.减少不必要的类型判断。例如上面提到的sum函数,要进行准确的功能实现,就需要对传入的形参值进行类型的判断,这样子就会增加了不必要的代码。
Flow静态类型检查方案
Flow是JavaScript的类型检查器。
简单安装及使用
初始化项目
yarn init --yes
安装flow-bin,安装在局部即可。
yarn add flow-bin --dev
安装完成后对flow进行初始化。
yarn flow init
然后便可以执行flow检查,以如下代码为例:
// @flow
function sum(a: number, b: number){
return a + b;
}
sum(100, 100);
sum('100',100)
这里需要注意要执行flow检查的代码需要在之前加入// @flow的标志,这里使用flow检查就会报错:
但是实际使用时,上述代码使用了类型注解会进行报错,因此我们需要将其自动移除。这里就需要安装一个模块:
yarn add flow-remove-types --dev
简单使用:
yarn flow-remove-types . -d dist
当然也可以配合babel使用:
yarn add @babel/core @babel/cli @babel/preset-flow
yarn babel src -d dist
安装开发工具插件
安装flow language support。
类型推断
例如如下代码:
/* 类型推断
* @flow
*/
function squre(n){
return n * n;
}
squre('100')
这里虽然没有为n添加类型注解,但是字符串显然不能相乘,因此这边代码也会在return语句进行报错。
类型注解
除了上述使用到的函数形参使用的类型注解外,类型注解还可以使用在变量和函数的返回值上,例如:
let a: number = 100
function foo(): number{
return 100
}
这边需要注意的是,若函数没有返回值,则应该设置为void。
原始类型
这里的原始类型和js的类似:
const a: string = '123'
const b: number = NaN
const c: boolean = false
const d: null = null
const e: void = undefined
const f: symbol = Symbol()
数组类型
可以通过泛型指定,或是通过方括号的方式指定。
const arr: Array<number> = [1,2,3]
const arr1: number[] = [1,2,3]
// 元组
const foo: [string, number] = ['111', 111]
对象类型
const obj1: { foo: string, bar: number} = { foo: '123', bar: 100}
const obj2: { foo?: string, bar: number} = { bar: 100}
const obj3: { [string]: string } = {}
obj3.key1 = 'value1'
obj3.key2 = 'value2'
函数类型
function foo(callback: (string, number) => void){
callback('string', 1000)
}
foo(function(str, n){
// str => string
// n => number
})
特殊类型
const a : '123' = '123'
const type: 'success' | 'warning' | 'danger' = 'success'
type StringOrNumber = string | number
const b: StringOrNumber = 123
const gender: ?number = undefined // 这里的?number相当于number | null | undefined
任意类型
Mixed
// mixed可以理解为任意类型,如string、number等等
function passMixed(value: mixed){
value.substr(1);
value * value
}
Any
// any也是可以为任意类型
function passAny(value: any){
value.substr(1);
value * value
}
区别
不同的是,any表示的是弱类型,mixed代表的是强类型,如上述相同的代码,使用mixed就会报错。
TypeScript语言规范与基本应用
基本使用
安装
yarn add typescript --dev
编译使用
yarn tsc demo01.ts
配置文件
安装项目运行配置。
yarn tsc --init
标准库声明
标准库就是内置对象所对应的声明,其实就是生成的tsconfig.json里的target属性,例如设置为es5的话,这时候使用es6的语法就会报错,而设置成es2015则不会。或者通过设置lib也可以:
"lib": ["ES2015","DOM"],
中文错误消息提示
yarn tsc --locale zh-CN
作用域问题
例如在其中一个ts文件中定义了一个const变量,然后在另一个文件中重复定义,就会报错。解决办法:1.可以使用立即执行函数等创建一个新的作用域;2.也可以通过export {},创建一个模块化的作用域。
object类型
ts中的object类型可以指对象、数组或是函数。
枚举类型
使用enum关键字定义:
enum PostStatus{
Draft = 0,
Unpublished = 1,
Published = 2
}
const post = {
title: 'hello ts',
content: 'hello world',
status: PostStatus.Draft
}
这里需要注意的是,如果enum中的字段没有定义值或是只定义部分的值,后续的值都是依次加一。当然枚举类型也可以是字符串,但是使用字符串,必须每个都要赋值。
隐式类型推断
其实就是事先不为变量添加类型注解,但是在赋值时,ts会隐式地进行类型的推断:
let age = 18 // 隐式推断为number
let str = 'string' // 隐式推断为string
类型断言
就是告诉ts自身定义的变量一定会是某种类型,例如:
const nums = [1,2,3,4]
const res = nums.find(i => i > 0)
const square = res * res
上述代码是会报错的,因为ts并不知道你res值一定是number,但如果进行了类型断言就不会发生报错:
const nums = [1,2,3,4]
const res = nums.find(i => i > 0)
// const num1 = res as number
const num1 = <number> res
const square = num1 * num1
类型断言一共有两种方式,但建议使用as。
接口
就是用来约束对象的结构,如果要实现接口,就一定要拥有这个接口的所有成员,当然也可以通过?设置可选成员。
interface People{
name: string
age: number
desc?: string
readonly id: string // 表示只读成员,不能进行修改
}
function getPer(people: People){
console.log(people.name)
console.log(people.age)
}
getPer({
name: 'lily',
age: 18,
id: '123'
})
接口还可以进行动态的定义,例如:
interface Stu {
[prop: string]: string | number
}
const stu: Stu = {}
stu.name = 'zhangsan'
stu.age = 18
类的使用
ts中的类与es6类似:
class Person {
name: string
age: number
constructor (name: string, age: number){
this.name = 'zhangsan'
this.age = 18
}
}
不同的是,这里的属性必须要进行初始化。并且ts中有访问修饰符,同java的public、private和proteced。还可以设置类的属性为只读。
类与接口
interface run {
run(distance: number) : void
}
interface eat {
eat(food: string) : void
}
class People implements run, eat{
run(num: number) {
console.log(num)
}
eat(str: string) {
console.log(str)
}
}
class Animal implements run, eat{
run(num: number) {
console.log(num)
}
eat(str: string) {
console.log(str)
}
}
抽象类
export {}
abstract class Animal {
run(dis: number): void {
console.log(dis)
}
abstract eat(food: string): void
}
class People extends Animal {
eat(str: string) {
console.log(str)
}
}
在抽象类中,可以定义普通方法和抽象方法,但是抽象方法在子类中一定要实现。
泛型
function createArray<T>(length: number, value: T): T[] {
const arr = Array<T>(length).fill(value)
return arr
}
console.log(createArray<number>(10, 10))
console.log(createArray<string>(5, '123'))
类型声明
使用declare声明:
declare function camelCase (input: string): string