概念
- 适用于大型项目,项目初期,会增加一些成本
快速上手
- 安装: yarn add typescript
- 编译命令: yarn tsc a.ts 编译后会生成一个js文件
const hello = (name:number) => {
console.log('hello '+ name);
}
hello('cxl') // 报错
配置文件
- 命令: yarn tsc --init
- .tsconfig.json
- 整个项目编译命令: yarn tsc
原始类型
const a: string = 'foobar'
const b: number = 100 // NaN Infinity
const c: boolean = true // false
const e: void = undefined
const f: null = null
const g: undefined = undefined
// 在非严格模式(strictNullChecks)下,
string, number, boolean 都可以为空
const d: string = null
const d: number = null
const d: boolean = null
标准库
- 内置对象所对应的声明文件
中文错误消息: tsc --locale zh-CN
作用域问题
(function () {
const a = 123
})()
或者是:
// 解决办法2: 在当前文件使用 export,也就是把当前文件变成一个模块
// 模块有单独的作用域
const a = 123
export {}
Object类型
- 不单指对象类型,这里包含对象、数组、函数类型
- 单指对象
const obj: { foo: number, bar: string } = { foo: 123, bar: 'string' }
数组类型
// 数组类型的两种表示方式
const arr1: Array<number> = [1, 2, 3]
const arr2: number[] = [1, 2, 3]
// 如果是 JS,需要判断是不是每个成员都是数字
// 使用 TS,类型有保障,不用添加类型判断
function sum (...args: number[]) {
return args.reduce((prev, current) => prev + current, 0)
}
sum(1, 2, 3) // => 6
元组类型
const tuple: [number, string] = [18, 'zce'] // 个数超出或者类型不对都会报错
const [age, name] = tuple
Object.entries({
foo: 123,
bar: 456
})
// 键值对就是元组 [string, number]
枚举类型
// 不取值,默认为0 1 2 如果Draft是6 则就是6 7 8
const enum PostStatus {
Draft,
Unpublished,
Published
}
// 加上const,在js中status展示: status: 0 /* Draft */ // 3 // 1 // 0
const post = {
title: 'Hello TypeScript',
content: 'Ty寒素peScript is a typed superset of JavaScript.',
status: PostStatus.Draft // 3 // 1 // 0
}
函数类型
参数
- 声明和调用的参数的个数必须一致
- 可选参数和剩余参数必须放最后
可选参数: b?: number 或者是 b: number = 10
function func1 (a: number, b: number = 10, ...rest: number[]): string {
return 'func1'
}
func1(100, 200)
func1(100)
func1(100, 200, 300)
函数表达式
const func2: (a: number, b: number) => string = function (a: number, b: number): string {
return 'func2'
}
任意类型any
- 不轻易使用any类型,兼容老代码可能会选择any类型
function stringify (value: any) {
return JSON.stringify(value)
}
stringify('string')
隐式类型推断
let age = 18 // number
age = 'string' // 报错
let foo // 推断为any类型
foo = 'string' // 不会报错
类型断言
// 假定这个 nums 来自一个明确的接口
const nums = [110, 120, 119, 112]
const res = nums.find(i => i > 0)
// const square = res * res // 直接用会报错,res可能是数字也可能是undefined
// 类型断言两种方式:
const num1 = res as number
const num2 = <number>res // JSX 下不能使用
interface接口
- 一种规范,一种契约
- 接口就是用来约束对象的结构,一个对象去实现一个接口,这个对象必须拥有这个接口当中所约束的所有成员
interface Post {
title: string
content: string
}
function printPost (post: Post) {
console.log(post.title)
console.log(post.content)
}
printPost({
title: 'Hello TypeScript',
content: 'A javascript superset'
})
- 可选成员、只读成员、动态成员
interface Post {
title: string
content: string
subtitle?: string // 可选成员
readonly summary: string // 只读成员
}
const hello: Post = {
title: 'Hello TypeScript',
content: 'A javascript superset',
summary: 'A javascript'
}
hello.summary = 'other' // 报错,不可修改
interface Cache {
[prop: string]: string // 动态成员
}
const cache: Cache = {}
cache.foo = 'value1'
cache.bar = 'value2'
类
基本使用
class Person {
name: string // = 'init name' // 先比js,ts必须先声明
age: number // 要嘛设置默认值,要嘛在构造函数中赋值,二选一
constructor (name: string, age: number) {
this.name = name
this.age = age
}
sayHi (msg: string): void {
console.log(`I am ${this.name}, ${msg}`)
}
}
类的访问修饰符
class Person {
public name: string // 默认是共有成员,最好加上public,增加可读性
private age: number // 私有成员,只允许内部访问
protected readonly gender: boolean // 受保护成员,允许继承
// readonly只读属性,只能在初始化赋值或者是构造函数中赋值,二选一,在类的内部还是外部都不能被修改了
constructor (name: string, age: number) {
this.name = name
this.age = age
this.gender = true
}
sayHi (msg: string): void {
console.log(`I am ${this.name}, ${msg}`)
console.log(this.age)
}
}
class Student extends Person {
private constructor (name: string, age: number) { // private 私有构造函数,不允许new实例化,只能通过内部的静态方法create去实例化
super(name, age)
console.log(this.gender)
console.log(this.age) // 报错 私有属性,只能在类Person中访问
}
static create (name: string, age: number) {
return new Student(name, age)
}
}
const tom = new Person('tom', 18)
console.log(tom.name)
console.log(tom.age) // 报错,私有成员外部不能访问
console.log(tom.gender) // 报错,受保护成员外部不能访问
const jack = Student.create('jack', 18)
const jack1 = new Student('jack', 18) // 类“Student”的构造函数是私有的,仅可在类声明中访问
类和接口
- 类与类之间共同的特征可通过接口抽象化
- 一般一个接口只约束一种能力
// 类与接口
interface Eat {
eat (food: string): void
}
interface Run {
run (distance: number): void
}
class Person implements Eat, Run {
eat (food: string): void {
console.log(`优雅的进餐: ${food}`)
}
run (distance: number) {
console.log(`直立行走: ${distance}`)
}
}
class Animal implements Eat, Run {
eat (food: string): void {
console.log(`呼噜呼噜的吃: ${food}`)
}
run (distance: number) {
console.log(`爬行: ${distance}`)
}
}
抽象类
- 接口:不包含具体的实现,抽象类:包含具体的实现
- 只能被继承,不能创建实例对象
// 抽线类
export {} // 确保跟其它示例没有成员冲突
abstract class Animal {
eat (food: string): void {
console.log(`呼噜呼噜的吃: ${food}`)
}
abstract run (distance: number): void // 抽象方法不需要方法体,子类必须要实现抽象方法
}
class Dog extends Animal {
run(distance: number): void {
console.log('四脚爬行', distance)
}
}
const d = new Dog()
d.eat('嗯西马')
d.run(100)
泛型
- 在定义函数、接口或类的时候不指定类型,在使用的时候再指定类型
- 在定义时把不能明确的类型变成一个参数,在使用的时候再传递这个类型参数
function createNumberArray (length: number, value: number): number[] {
const arr = Array<number>(length).fill(value)
return arr
}
function createStringArray (length: number, value: string): string[] {
const arr = Array<string>(length).fill(value)
return arr
}
function createArray<T> (length: number, value: T): T[] {
const arr = Array<T>(length).fill(value)
return arr
}
// const res = createNumberArray(3, 100)
// res => [100, 100, 100]
const res = createArray<string>(3, 'foo')
类型声明
- 一个成员在定义的时候由于种种原因没有一个明确的声明,在使用的时候单独对它做出明确的声明
- 常用的npm模块都已经提供了对应的声明,只需要安装即可
- 引用第三方模块,如果没有进行类型声明,就需要引入对应的类型声明,再没有的话,就要使用declare语句进行类型声明
import { camelCase } from 'lodash' // 需要安装 @types/lodash
import qs from 'query-string' // 不需要单独安装,模块中已包含对应的类型声明文件
qs.parse('?key=value&key2=value2')
// declare function camelCase (input: string): string // 没有类型声明文件需要通过declare进行声明
const res = camelCase('hello typed')