概述
TypeScript是JavaScript的超集,也就是说TypeScript不仅包含了JavaScript的全部内容,同时也包含其他内容。
TypeScript = JavaScript + 类型系统 + ES6+新特性支持。
原始类型
TypeScript原始类型可以和JavaScript基础类型一一对应。
const a: number = 1 // NaN
const b: string = 'foo'
const c: boolean = true // false
const d: void = undefined
const e: null = null
const f: undefined = undefined
const g: symbol = Symbol()
数组类型
const arr1: Array<number> = [1, 2]
const arr2: string[] = ['foo', 'bar']
元组类型
指的是明确数量和类型的数组。
const arr: [number, string] = [1, 'foo']
// 可以通过以下方式正常获取数组中的元素
console.log(arr[1])
const [num, str] = arr
object类型
const obj: object = {}
obj['name'] = 'zhangsan'
枚举类型enum
枚举类型指的是通过enum关键字定义一组键值对数据,数据定义完成后只能读取,不能修改。
// 文章状态
enum PostStatus {
// 草稿
Draft = 0,
// 未发布
Unpublished = 1,
// 已发布
Published = 2
}
const post = {
title: 'typescript',
status: PostStatus.Published
}
// 直接通过enum声明的枚举类型可以用数字获取枚举名称
console.log(PostStatus[0]) // Draft
常量枚举:在enum关键字前面加上const
// 文章状态
const enum PostStatus {
// 草稿
Draft = 0,
// 未发布
Unpublished = 1,
// 已发布
Published = 2
}
// 此时再用数字获取枚举值就会报错
console.log(PostStatus[0])
Never
never表示永远不存在值的类型,通常用于总是抛出异常或者永远不会有返回值的函数的返回值类型。
// 抛出异常
function fn(): never {
throw new Error()
}
// 永远不会有返回值
function fn1(): never {
while (true) { }
}
函数类型
// 可以在声明函数的时候用注解的方式为参数和返回值添加类型
function sum(a: number, b: number): number {
return a + b
}
// 也可以用箭头函数表示函数类型
const sum1: (a: number, b: number) => number
= function (a, b) {
return a + b
}
// 函数声明后,调用时只能传入与声明相对应的参数类型和参数数量
// 或者使用可选参数和剩余参数
function test(a?: number, ...rest: number[]) {
// todo
}
在TypeScript中可以为同一个函数提供多个函数类型定义来进行函数重载。
// 重载一
function test(x: number): number;
// 重载二
function test(x: string): string;
// 并不是重载的一部分,只是实现逻辑
function test(x: any): any {
if (typeof x === 'number') {
return x
} else if (typeof x === 'string') {
return x
}
}
test(1)
test('foo')
任意类型
ts中可以使用any表示任意类型,提供any主要是为了兼容老的js代码。
let a: any = 1
// 可以改变a的类型
a = 'foo'
类型推断
当未改变量指明类型的时候,ts可以通过参数值推断参数类型。
// 此时会自动推断a的类型为number
const a = 1
类型断言
ts中可以手动指定变量的类型。
const a: number | undefined = undefined
// 将a的类型转为指定为number
let b: number = a as number
let c: number = <number>a
接口
可以通过interface关键字设置对象必须满足某种要求。
const enum PostStatus {
// 草稿
Draft = 0,
// 未发布
Unpublished = 1,
// 已发布
Published = 2
}
// 要求文章必须具备title,status, id属性,可以有subtitle属性,其中id为只读属性
interface Post {
title: string
subtitle?: string
status: PostStatus,
readonly id: number
}
let obj: Post = {
title: 'typescript',
status: PostStatus.Draft,
id: 1
}
// 只读属性不能修改
obj.id = 2
针对cache这种属性不确定的对象,可以使用动态成员。
interface ICache {
// 要求属性和属性值均为字符串
[key: string]: string
}
let cache: ICache = {}
// 可以在使用的时候给对象添加符合要求的属性
cache['foo'] = 'bar'
类
TypeScript在ES2015的class类基础上添加了一些关键字,用于描述类的类型。
class Person {
name: string
// 私有属性,外部无法访, 可以添加readonly使其变为只读属性
private readonly type: string = 'person'
// protected 受保护,只能在内部和子类内部使用
protected field: string = 'foo'
constructor(name: string) {
this.name = name
}
sayHi(msg: string) {
// 可以在类内部访问私有属性
console.log(`my name is ${this.name}, i am a ${this.type}, ${msg}`)
}
}
class Student extends Person {
constructor(name: string) {
super(name)
}
sayHi() {
// 能访问父类受保护的属性,但是不能访问私有private属性
console.log(this.field)
}
}
当privite用在constructor前面时,那么这个类就不能在其他地方使用new生成实例。
class Book {
title: string;
private constructor(title: string) {
this.title = title
}
// 可通过静态方法提供实例化对象的途径
static create(title: string) {
return new Book(title)
}
}
const ts = Book.create('TypeScript')
同时,类可以继承接口,表示类需要具备某些方法或者属性。
interface IBook {
getContent(): string
}
// 类可以继承多个接口
class Book implements IBook {
title: string;
constructor(title: string) {
this.title = title
}
// 必须实现接口
getContent(): string {
return `TypeScript is a Language`
}
}
抽象类
抽象类声明和普通类相似,只不过抽象类不能实例化,同时抽象类中的抽象方法需要子类实现。
abstract class Person {
name: string;
// 抽象属性
abstract field: string
constructor(name: string) {
this.name = name
}
// 抽象方法
abstract sayHi(msg: string): void
}
class Student extends Person {
field: string = 'test';
sayHi(msg: string): void {
console.log(msg)
}
}
泛型
泛型可以理解为将可变化的类型当作一个参数
const createNumberArray = function(length: number, value: number): number[] {
return Array(length).fill(value)
}
const arr = createNumberArray(2, 1) // [1, 1]
const createStringArray = function(length: number, value: string): string[] {
return Array(length).fill(value)
}
const arr1 = createStringArray(2, 'foo') // [foo, foo]
在上面的例子中,createNumberArray和createStringArray除了参数和返回值的类型不同之外,其余都一样,此时就可以使用范型。
function createArray<T>(length: number, value: T): T[] {
return Array(length).fill(value)
}
// 在使用时指定类型
const arr = createArray<number>(2, 1) // [1, 1]
const arr1 = createArray<string>(2, 'foo') // [foo, foo]
- 在声明范型的时候,可以为类型添加约束。
interface Person {
name: string
}
// 通过extends为类型添加约束,要求类型必须实现了Person接口
function intro<T extends Person>(person: T) {
return person.name
}
intro({ name: 'zs' }) // ok
intro(5) //error number类型没有实现Person
- 声明范型的时候,可以添加一个被另一个类型参数约束的类型参数。
interface Person {
name: string,
age?: number
}
// 此范型要求T必须实现Person, U必须是T的属性中的某一个
// keyof是索引类型查询操作符,假设T是一个类型,那么keyof T产生的类型是T的属性名称字符串字面量类型构成的联合类型
function intro<T extends Person, U extends keyof T>(person: T, key: U) {
return person[key]
}
intro({ name: 'zs' }, 'name')
// 相当于
function intro<T extends Person, U extends 'name' | 'age'>(person: T, key: U) {
return person[key]
}
- 在范型中还可以使用类类型
class Person {
constructor() { }
}
function create<T>(c: { new(): T; }) {
return new c()
}
const zs = create<Person>(Person)
类型声明declare
import { camelCase } from 'lodash'
// 当引用类似lodash这种没有类型声明文件的第三方库时,如果不明确声明引入的变量的类型,将会报错
declare let camelCase = (str?: string) => string
console.log(camelCase('hello typescript'))
通常类型声明会放入*.d.ts文件中
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。