最近在学习typeScript,因为公司估计需要使用。同样是学习笔记,写文章的意图就在于复习总结一下之前的学习,顺带着积累写文章的感觉,在这个人人都是自媒体的时代,也能不被落的太远。
ts简介
ts是拥有类型系统的js的超集,可以编译成js。它有三个特性:
语言类型简介
语言类型分类有两个维度。
一个是分为强类型语言和弱类型语言:
一个是分为静态类型语言和动态类型语言:
这里其实可以举个栗子🌰:
// c++
class C {
public:
int x;
int y;
}
// add function
int add(C a, C b) {
return a.x + a.y + b.x + b.y
}
// 在针对内存分配时,add函数中的形参在编译时就确定了属性的偏移量,用偏移量代替了属性名访问
// js
class C {
constructor(x,y){
this.x = x
this.y = y
}
}
// add function
function add(a,b) {
return a.x + a.y + b.x + b.y
}
// 针对add函数,在编译的时候会存储属性名x、y,并在运行动态计算偏移量
ts数据基本类型
// boolean
let bool: boolean = true
// Number
let num: number = 1
// string
let str: String = 'str'
// Symbol
let sym: Symbol = Symbol()
// null是任何类型的子类型
// null,只能为null
let nul: null = null
// undefined,只能为undefined
let undef: undefined = undefined
// Array,数组还要确保元素的类型
let arr: Array<number> = [1,2,3]
let arr2: number[] = [4,5,6]
// Object
// 这样的方式,不可更改属性的值
let obj: Object = {x:1, y:2}
let obj2: {x: number, y: number} = {x:1, y:2}
// function
let fun = (x: number, y: number) => x+y
// 或者声明返回值类型,但是ts会自动推测
let fun2 = (x: number, y: number): number => x+y
// 还可以先定义,后赋值
let fun3 = (x: number, y: number) => number
// 形参名可以不同
fun3 = (a: number, b:number) => a+b
// 元组tuple,不仅确定了类型还确定了元素数量
let tuple: [number, string] = [1,'2']
// 可以越界添加
tuple.push(3)
// 但是不可以越界访问
tuple[2] // 报错
// any,任何类型,这就和js中定义变量没其他不同了
let any1: any = '1'
any1 = 2
// void,什么类型也不是
// void作为操作符,可以使表达式为undefined
void 1+2 // 输出undefined
// 函数返回值为void
let void_fun = (): void => {}
// 可以被赋值undefined和null(设置strictNullChecks为false)
let unusable = undefined
unusable = null
// never,表示不存在这个类型
// never是任何类型的子类型
let nev = (): never => {
throw Error("this is a err")
}
let nev2 = (): never => {
while(true) {}
}
// 联合类型,每种类型都是或的关系
let union: number | string | boolean
ts枚举类型
枚举类型在js中是没有的,是一组具有名称的常量集合。将程序中存在的硬编码或者将来可能改变的常量抽取出来形成一个枚举类型,方便管理。
// 各个属性自0递增
enum Role {
Developer,
Reporter,
Owner,
}
Role.Development // 0
Role.Owner // 2
// 可以自定义指定属性值
enum Role {
Development=1,
Reporter,
Owner,
}
Role.Owner // 3
再者数字枚举,其实是一个对象,符合反向映射,如上面的Role其实是:
// 字符串枚举
enum Message {
success='success',
fail='fail'
}
字符串枚举不会反向映射,如上面的Message:
enum Answer {
X,
Y='No',
// 注意,Z必须赋初始值
Z=1,
H,
}
// 使用const标识符定义
// 在编译时会被移除,只可访问属性,更加节省时间
const enum Info {
success,
fail,
}
// 枚举成员未赋初始值
enum E {a,b}
let a: E = 1
let b: E.b = 2
// 枚举成员赋初始值
enum F {a=1, b='str'}
// 注意字符串只能赋已有的值,且如果指定了某个成员类型则必须为这个成员的值
let c: E.b = 'str' // 报错
let d: E.b = E.b // 正确
// 不同类型之间不能进行比较
a === b // 报错
枚举成员是只读属性,不能被赋值。在运行时枚举就是一个对象。
// 和常量枚举的不同点在于未被赋初值的成员会被当作计算值
declare enum Enum {
x=1,
y, // 当作计算值
z=3
}
ts接口类型
使用interface定义,用来约束对象、函数、类的结构和类型。
interface List {
// 加上readonly即变为只读属性
readonly id: number,
name: string,
// 加上?就为可选属性
age?: number,
// 使用索引
[x: string]: any
}
let data: List = {
id: 0,
name: 'zhangzongwei'
}
let arr: List[] = [
{id: 0, name: 'duanshuqing'},
{id: 1, name: 'zhangzongwei', age: 25}
]
// 当不做数据类型约束时可以添加其他未定义的属性,但是不允许访问
let data2 = {
id: 1,
name: 'zhangzongwei',
sex: '男'
}
function render(result: List) {
console.log(result.id,result.name)
console.log(result.sex) // 报错
}
// 当添加了其他属性,以下面的方式传入render时,会报错
render(
{id: 0, name: 'zhangzongwei', sex: '男'}
)
// 解决方式:
// 1. 使用断言,就是确定地告诉ts编译器,我清楚传入的是什么类型
render(<List>{...})
render({...} as List)
// 2. 使用索引,见上面
// 3. 赋值给一个变量再传入
当你不确定对象中有多少个属性的时候,就可以使用索引类型。
// 数字索引
interface Names {
[x: number]: string
}
let name: Names = ['zhang','zongwei']
// 字符串索引
interface Address {
[y: string]: string
}
let addr: Address = {
province: 'henan',
city: 'xinyang'
}
// 字符串接口有两种访问方式
addr.province
addr['city']
// 两者可以混用,number返回的属性值一定要和string返回的属性值一样
// 因为当通过number索引时,也是先转换为string再去索引,所以返回的属性值类型要相同
interface Info {
[x: string]: string,
[y: number]: string
}
interface render {
(result: string) => string
}
let ren: render = (x: string) => x
// 也可以不传入参数类型,因为ts会自动推导
let ren2: render = (x) => x
interface Lib {
(): void,
version: string,
getVersion(ver: string): string
}
let lib: Lib = (() => {}) as Lib
lib.version = '1.0'
lib.getVersion = (ver: string) => ver
参考资源