强类型语言:不允许改变数据类型,除非强制转换数据类型
弱类型语言:变量允许被赋予任意类型
静态类型语言:在编译阶段确定所有变量的类型
动态类型语言:在执行阶段确定所有变量的类型
# 创建 ts 配置文件 tsconfig.json
$ tsc --init
Typescript JavaScript
Boolean Boolean
Number Number
String String
Array Array
Function Function
Object Object
Symbol Symbol
undefined undefined
null null
void
any
never
元组
枚举
高级类型
// 作用:类似强类型语言中的类型声明
// 语法
[变量|函数]: type
enum _enum{
成员
}
// JS 描述枚举的方式
let _enum
(function (_enum) {
_enum[_enum["成员"] = 1] = "成员";
}
)(_enum || (_enum = {}))
function add1(x: number, y: number ){
return x + y;
}
let add2: (x: number, y: number) => number
type add3 = (x: number, y: number) => number;
interface add4 {
(x: number, y: number): number
}
// 重载
function add3(...rest: number[]): number;
function add3(...rest: string[]): string;
function add3(...reset: any[]): any {
if (typeof reset[0] === 'number') {
return 1;// TODO
}
if (typeof reset[0] === 'string') {
return '1'; //TODO
}
}
// 与 ES 不同的是 为成员属性添加了类型注解
// 添加了 run 方法 默认返回值是 void
// 构造函数的返回值默认是 Dog 也就是这个类本身
class Dog {
constructor(name: string){
this.name = name;
}
name: string
run(){}
}
// 不包含内部属性
console.log(Dog.prototype) // {run: f, constructor: f}
// 内部属性在实例上,不在原型上
// 实例的属性必须具有初始值,或在构造函数中被初始化
let dog = new Dog('wangwang');
console.log(dog);// Dog {name: 'wangwang'}
class Husky extends Dog {
constructor(name: string, color: string) {
super(name);
this.color = color;
}
color: string
// 类的成员修饰符,是对ES的扩展
// ES 中默认都是 public
// private 不能被实例和子类调用
// private constructor 表示既不能被实例化也不能被继承
// protected 只能在类和子类中访问
// protected constructor 表示只能被继承,不能被实例化,声明了一个基类
// readonly 不能被更改,一定要被初始化
// static 只能通过类名调用
}
// 抽象类 TS 对 ES 的扩展
// 只能被继承,而不能被实例化的类
abstract class Animal {
// 抽象类中的方法可以有具体的实现
eat() {
console.log('eat');
}
// 抽象类中的方法也可以不具体实现,称为抽象方法
// 抽象方法的好处是可以在子类中有其他的实现
abstract sleep(): void
}
// 抽象类可以实现多态
// 多态: 在父类中定义一个抽象方法,在多个子类中对这个方法有不同的实现
class Cat extends Animal {
sleep(){
console.log('cat cleep');
}
}
let cat = new Cat();
let animals: Animal[] = [dog, cat];
// TS 的this 类型,实现链式调用
class WorkFlow {
work1(){
// TODO
return this;
}
work2(){
// TODO
return this;
}
}
new WorkFlow().work1().work2()
class Myflow extends WorkFlow {
next(){
return this;
}
}
// 子父类联合调用
new Myflow().next().work1().next();
// 类类型接口
interface Human {
name: string;
eat(): void;
}
// 类实现接口时必须实现接口中声明的所有属性
// 接口只能约束类的公有(public)成员
// 接口不能约束 constructor
class Asian implements Human {
constructor(name: string) {
this.name = name;
}
name: string
eat() {}
}
// 接口可以和类一样 相互继承
// 一个接口可以继承多个接口
interface Man extends Human {
run(): void
}
interface Child {
cry(): void
}
interface Boy extends Man, Child {}
let boy: Boy {
name: 'leeyw',
run() {},
eat() {},
cry() {}
}
// 接口只有成员结构,但没有具体的实现
class Auto {
state = 1
}
interface AutoInterface extends Auto {}
class C implements AutoInterface {
state = 1
}
class Bus extends Auto implements AutoInterface {
// 这里不需要设置 state 属性,因为 Bus 继承了 Auto ,作为 Auto 子类也继承了 state 属性
}
泛型: 不预先确定的数据类型,具体的类型在使用的时候才能确定。
好处
- 函数和类可以轻松地支持多种类型,增强程序的扩展性
- 不必写多条函数重载,冗长的联合类型声明,增强代码可读性
- 灵活控制类型之间的约束
// Q: 为什么不用 any
// A: any 类型丢失了一些信息,也就是类型之间的约束关系,忽略了输入参数类型和函数的返回值类型必须是一致的
// 声明
function fn<T, C>(k: T, C: V): T {
return k;
}
// 调用
// 调用时指明
fn<number, number>(1, 2);
// 调用时利用 ts 的类型推断
fn(1, 2);
// 定义泛型函数类型
type Log = <T>(value: T) => T
let myLog: Log = log;
// 定义泛型接口
interface Log {
<T>(value: T): T
}
// 有默认类型
interface Log <T> {
(value: T): T
}
let myLog: Log<number> = log;
// 没有默认类型
interface Log <T = string> {
(value: T): T
}
let myLog: Log = log;
// 泛型约束类的成员
// 泛型不能约束 static 成员
class Log<T> {
run(value: T) {
console.log(value);
return value;
}
}
let log1 = new Log<number>();
let log2 = new Log();
interface Length {
length: number
}
function log<T extends Length>(value: T): T {
console.log(value, value.length);
return value
}
log([]); // √
log('11'); // √
log(1); // × 没有 length 属性
Typescript 类型检查机制: TypeScript 编译器在做类型检查时,所秉承的一些原则,以及表现出的行为。辅助开发,提高开发效率;类型推断、类型兼容、类型保护
不需要指定变量的类型(函数返回值类型),TypeScript 可以根据某些规则自动地为其推断出一个类型。
let a = 1;
let b = [1];
let c = (x = 1) => x + 1;
当需要从多个类型中推断出一个类型时, TS 会尽可能的推断出一个兼容当前所有类型的通用类型
let b = [1, null]; // let b: (number | null)[]
// tsconfig.json
// "strictNullChecks": false
let b = [1, null]; // let b: number[]
window.onkeydown = (event) => {
} // (parameter) event: KeyboardEvent
// 类型断言
interface Foo {
bar: number
}
let foo = {} as Foo;
foo.bar = 1
当一个类型 Y 可以被赋值给另一个类型 X 时,我们就可以说 X 兼容 Y
X 兼容 Y: X(目标类型) = Y(源类型)
类型兼容性的例子广泛存在于,接口,函数,类中
// 接口兼容性
interface X {
a: any;
b: any;
}
interface Y {
a: any;
b: any;
c: any;
}
let x: X = {a: 1, b: 2}
let y: Y = {a: 1, b: 2, c: 3}
x = y // √
y = x // ×
// 函数兼容性
type Handler = (a: number, b: number) => void
function hof(handler: Handler) {
return handler;
}
// 1) 参数个数,目标函数(Handler)参数个数多于原函数(hof)参数个数
// 2) 参数类型
interface Point3D {
x: number;
y: number;
z: number;
}
interface Point2D {
x: number;
y: number;
}
let p3d = (point: Point3D) => {}
let p2d = (point: Point2D) => {}
p3d = p2d; // :)
p2d = p3d; // :(
// 3) 返回值类型 目标函数的返回值类型必须与原函数返回值类型相同,或者为其子类型
TypeScript 能够在特定的区块中保证变量属于某种确定类型
可以在此区块中放心的引用此类型的属性,或者调用此类型方法
enum Type { Strong, Week}
class Java {
helloJava() {
console.log('Hello Java');
}
}
class JavaScript {
helloJavaScript() {
console.log('Hello JavaScript');
}
}
function getLanguage(type: Type) {
let lang = type === 'Type.Strong' ? new Java() : new JavaScript();
// 使用类型断言 麻烦!
// if ((lang as Java).helloJava) (lang as Java).helloJava();
// else (lang as JavaScript).helloJavaScript();
// 使用 instanceof 判断某个实例是不是属于某个类
// if (lang instanceof Java) {
// // 在这个区块中,可以保证 lang 一定是 Java 实例
// } else {
// // 在这个区块中,可以保证 lang 一定是 JavaScript 实例
// }
// 使用 in 关键词, 可以判断某个属性是不是属于某个对象
}
编译工具 -> ts-loader / @babel/preset-typescript
代码检测工具 -> eslint + typescript-eslint / babel-eslint
单元测试 -> ts-jest / babel-jest
// math.ts
function add(a: number, b: number): number {
return a + b;
}
function sub(a: number, b: number): number {
return a - b;
}
module.exports = {
add,
sub
}
// test/math.test.ts
// 使用 ts-jest 可以在测试中进行类型检查
const math = require('src/math');
test('add: 1 + 1 = 2', () => {
expect(math.add(1, 1)).toBe(2);
})
test('sub 1 - 2 = -1', () => {
expect(math.sub(1, 2)).toBe(-1);
})