1.安装 typescript 运行环境
需要安装:npm node typescript
2.运行
为方便运行 typescript转为javascript
直接使用ts-node 运行 (使用需安装)
静态类型的深度理解
基础类型//null,undefind,symbol,boolean,void(没有返回值),never(阻止执行),number,string
let count :number;
count = 123;
// 还有就是两个奇葩兄弟两,undefined 和 null
let u: undefined = undefined
let n: null = null
// 注意 undefined 和 null 是所有类型的子类型。也就是说 undefined 类型的变量,可以赋值给 number 类型的变量:
let num: number = undefined
any 类型
let notSure: any = 4
notSure = 'maybe it is a string'
notSure = 'boolean'
// 在任意值上访问任何属性都是允许的:
notSure.myName
// 也允许调用任何方法:
notSure.getName()
对象类型
//{},Class.function,[]
const func = (str:string):number =>{
return parseInt(str,10);
}
const func:(str:string)=>number=(str)=>{
return parseInt(str,10);
}
//其他类型 case
let temp:number | string = 123;
temp ='456'
//type annotation 类型注解,我们告诉TS变量是什么类型
let count :number;
count =123;
//type inference 类型推断,TS会自动分析变量的类型
let data =123;
//如果 ts能够自动分析变量类型,我们就什么也不需要做了
//如果ts无法分析变量类型,我们就需要使用类型注释
function get(a:number,b:number)
{
return a+b;
}
const total = getTotal(1,2);
函数相关类型
function get(a:number,b:number):number
{
return a+b;
}
const total = get(1,2);
//解构加类型注解
function add(
{ first, second }: { first: number, second: number }): number{
return first + second;
}
//函数定义
普通定义
1.function hello(){}
2.const hello =function(){}
3.const hello=()=>{}
高级定义
1.
const func = (str: string): number => {
return parseInt(str, 10);
}
2.
const func: (str: string) => number = (str) => {
return parseInt(str, 10);
}
数组
const arr:(number | string)[]=[1,'2',3];
const stringarr: string[]=['a','b','c'];
//类型别名 type alias
type User ={name:string,age:number};
const objectArr :User[]=[{
name:'123',
age:28
}]
//元组 tuple
const arr:[string,string,number]=['123','456',18 ];
处理csv 数据
const arr :[string,string,number][]=[
['1','2',3],
['4','5','6'],
[],
]
//最简单的方法是使用「类型 + 方括号」来表示数组:
let arrOfNumbers: number[] = [1, 2, 3, 4]
//数组的项中不允许出现其他的类型:
//数组的一些方法的参数也会根据数组在定义时约定的类型进行限制:
arrOfNumbers.push(3)
arrOfNumbers.push('abc')
// 元祖的表示和数组非常类似,只不过它将类型写在了里面 这就对每一项起到了限定的作用
let user: [string, number] = ['viking', 20]
//但是当我们写少一项 就会报错 同样写多一项也会有问题
user = ['molly', 20, true]
Interface接口//帮助做语法校验的工具
interface Person {
readonly name;string;//readonly 只读模式
age?:string; // ? 使得 age 可有可无
[propName:string]:any;//可以添加其他属性,例如这个是以any的形式的属性
say(): string;//say表示接口里面要有一个方法,这个方法的返回值必须是string
}
//interface 与类型别名相似 但是interface只能代表 函数和对象
type person1 = string;
const getPersonName = (person :Person)=>{
console.log(person.name )
}
const person = {
name:'dell'
}
getPersonName(person)
// 我们定义了一个接口 Person
interface Person {
name: string;
age: number;
}
// 接着定义了一个变量 viking,它的类型是 Person。这样,我们就约束了 viking 的形状必须和接口 Person 一致。
let viking: Person ={
name: 'viking',
age: 20
}
//有时我们希望不要完全匹配一个形状,那么可以用可选属性:
interface Person {
name: string;
age?: number;
}
let viking: Person = {
name: 'Viking'
}
//接下来还有只读属性,有时候我们希望对象中的一些字段只能在创建的时候被赋值,那么可以用 readonly 定义只读属性
interface Person {
readonly id: number;
name: string;
age?: number;
}
viking.id = 9527
类的的定义与继承
class Person{
name: string = "dell"//定义一个name属性
getName() {//定义一个方法
return this.name;
}
}
//通过类创建一个实例
const person = new Person();
console.log(person.getName());
//类的继承
class Teacher extends Person{
getTeacherName() {
return 'Teacher';
}
getName() {//类的重写
super.getName();//super 相当于Person这个父类,调用了Person里面的方法,当父类的方法被重写之后,如果想调用被重写之前的父类方法,就可以使用super
return 'leen';
}
}
const teacher = new Teacher();
console.log(teacher.getName());
console.log(teacher.getTeacherName());
枚举
1.创建枚举
enum 枚举名称{成员1,成员2,...}
创建枚举通过enum关键字创建,关键字后面加上枚举名称,在后面加上花括号,花括号里面放入枚举的成员,枚举成员可以有多个。
枚举名称、成员名称首个字母大写,多个成员之间用逗号隔离。
示例:
//家庭枚举:妈妈,爸爸和我
enum Family {Mom,Father,Me}
2.使用枚举
枚举是一种类型,可以作为变量的类型注释。
enum Gender {Female,Male}
let userGender:Gender
访问枚举中的成员,作为变量的值
userGender = Gender.Female
userGender = Gender.Male
值得注意的是:枚举中的成员只能访问,不能赋值。
Gender.Female = '男' //错误
// 数字枚举,一个数字枚举可以用 enum 这个关键词来定义,我们定义一系列的方向,然后这里面的值,枚举成员会被赋值为从 0 开始递增的数字,
enum Direction {
Up,
Down,
Left,
Right,
}
console.log(Direction.Up)
// 还有一个神奇的点是这个枚举还做了反向映射
console.log(Direction[0])
// 字符串枚举
enum Direction {
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT',
}
const value = 'UP'
if (value === Direction.Up) {
console.log('go up!')
}
类的访问类型和构造器
// private , protected, public
//public 允许我在类得内外被调用
//private 允许在类内被调用
//protected 允许在类内及继承的子类中使用
class Person {
protected name: string;
public sayHi() {
this.name;
console.log('hi');
}
}
const person = new Person();
person.name = 'dell';
person.sayHi();
class Teacher extends Person{//继承的子类,Person 是父类Teacher是子类
}
//constructor 构造器
class Person {
//传统写法
//public name:string;
constructor(name: string) {
this.name = name;
}
//简化写法
constructor(public name: string) {
this.name = name;
}
}
const person = new Person('dell');
console.log(person.name)
//一个类被子类调用,如果子类要用构造器的话,调用父类构造器时用super并且按照父类的参数要求传参数
class Person{
constructor(public age:string){}
}
class Teacher extends Person{
constructor(public age:nmuber){
super('dell);
}
}
const teacher = new Teacher(28);
console.log(teacher.age)
console.log(teacher.number)
静态属性
Setter/Getter
通过set/get,完成对私有属性的开放和封装。
class Person4GetOrSet {
// 一般用下划线_表示私有属性
constructor(private _name: string) {}
// 定义对外开放属性
get name() {
return this._name;
}
// 赋值定义,这边也可以做一些赋值过滤处理
set name(name: string) {
this._name = name;
}
}
const person = new Person4GetOrSet("张三");
console.log(person.name);
person.name = "李四";
console.log(person.name);
抽象类
编译运转过程
typescript中的配置文件
tsconfig.ts
联合类型和类型保护
1. 联合类型
什么是联合类型
联合类型(Union Types)表示取值可以为多种类型中的一种。
联合类型使用 | 分隔每个类型。
let testNumber: string | number;
testNumber = 'six';
testNumber = 8;
// 这里的string | number的含义是,允许testNumber的类型是string或者number,但是不能是其他类型。
访问联合类型的属性或方法
当TypeScript不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问联合类型的所有类型里共有的属性或方法。
interface Bird {
fly: boolean;
sing: () => {};
}
interface Dog {
fly: boolean;
bark: () => {};
}
function trainAnialCommon(animal: Bird | Dog) {
animal.fly;
}
2. 类型保护
什么是类型保护
类型保护就是一些表达式,它们会在运行时检查以确保在某个作用域里的类型。
为什么需要类型保护
正如上文出现的联合类型,当一个变量具备多种类型的时候,虽然我们知道代表着什么类型,但是编译器是死的,它是无法知道具体的类型,这个时候就需要我们手动就处理,保护我们的程序能够正常编译运行。
怎么实现类型保护?
方式一: 使用as 类型断言的方式
interface Bird {
fly: boolean;
age: number;
sing: () => {};
}
interface Dog {
fly: boolean;
age: string;
bark: () => {};
}
/**
* 使用as 类型断言的方式
* @param animal
*/
function trainAnial(animal: Bird | Dog) {
// 写法一
if (animal.fly) {
(animal as Bird).sing();
} else {
(animal as Dog).bark();
}
// 写法二
if (animal.fly) {
(<Bird>animal).sing();
} else {
(<Dog>animal).bark();
}
}
方式二:in 语法来做类型保护
/**
* in 语法来做类型保护
* @param animal
*/
function trainAnialSecond(animal: Bird | Dog) {
if ("sing" in animal) {
animal.sing();
} else {
animal.bark();
}
}
方式三:typeof 语法来做类型保护
/**
* typeof 语法来做类型保护
* @param first
* @param second
*/
function add(first: string | number, second: string | number) {
if (typeof first === "string" || typeof second === "string") {
return `${first}${second}`;
}
return first + second;
}
方式四:使用 instanceof 语法来做类型保护
/**
* 使用 instanceof 语法来做类型保护
*/
class NumberObj {
conut: number;
}
function addSecond(first: object | NumberObj, second: object | NumberObj) {
if (first instanceof NumberObj && second instanceof NumberObj) {
return first.conut + second.conut;
}
return 0;
}
Enum枚举类型
1. 什么是枚举
枚举(Enum)类型用于取值被限定在一定范围内的场景,TypeScript支持数字的和基于字符串的枚举。
关键字:enum
2. 怎么去定义枚举
数字枚举定义
enmu Status {
ON,
OFF
}
// 访问取值
console.log(Status.ON); // 0
// 访问取枚举
console.log(Status[0]); // ON
默认数值从0开始排序,当然和可以自定义,如果ON表示1,其余的成员会从1开始自定增长。
注意:手动赋值时,注意值覆盖的情况,这种ts是无法检测报错的。
enum Months {
JAN = 4,
FEB = 1,
MAR,
APR,
MAY,
JUN,
JUL,
}
手动赋值也可以是小数,比如FEB=1.5,但是递增的步长依旧会是1
如果手动赋值的枚举项不是数字,此时需要使用类型断言来让tsc无视类型检查。
JUL = <any>"jul",
函数泛型
generics 泛指的类型
泛型的定义和使用
使用泛型可以创建泛型变量、泛型函数、泛型接口、泛型类,不可以创建泛型枚举和泛型命名空间
创建泛型变量,表示这些参数是任意或者所有类型,只有使用的时候,才能被确定是什么类型。
// first: T 和 second: T 就是一个泛型变量
function join<T>(first:T,second:T){
return `${first} + ${second}`;
}
join<number>(1,1);//使用的时候就确定number类型
//多个泛型
function join<T,p>(first:T,second:p){
return `${first} + ${second}`;
}
join<number,number>(1,1)
泛型约束
function echoWithArr<T>(arg: T): T {
console.log(arg.length)
return arg
}
// 上例中,泛型 T 不一定包含属性 length,我们可以给他传入任意类型,当然有些不包括 length 属性,那样就会报错
interface IWithLength {
length: number;
}
function echoWithLength<T extends IWithLength>(arg: T): T {
console.log(arg.length)
return arg
}
echoWithLength('str')
const result3 = echoWithLength({length: 10})
const result4 = echoWithLength([1, 2, 3])
泛型与类和接口
class Queue {
private data = [];
push(item) {
return this.data.push(item)
}
pop() {
return this.data.shift()
}
}
const queue = new Queue()
queue.push(1)
queue.push('str')
console.log(queue.pop().toFixed())
console.log(queue.pop().toFixed())
//在上述代码中存在一个问题,它允许你向队列中添加任何类型的数据,当然,当数据被弹出队列时,也可以是任意类型。在上面的示例中,看起来人们可以向队列中添加string 类型的数据,但是那么在使用的过程中,就会出现我们无法捕捉到的错误,
class Queue<T> {
private data = [];
push(item: T) {
return this.data.push(item)
}
pop(): T {
return this.data.shift()
}
}
const queue = new Queue<number>()
//泛型和interface
interface KeyPair<T, U> {
key: T;
value: U;
}
let kp1: KeyPair<number, string> = { key: 1, value: "str"}
let kp2: KeyPair<string, number> = { key: "str", value: 123}
类中的泛型以及泛型类型
类里面最基础的泛型示例
class DataManager<T>{
constructor(private data:T[]){}
getItem(index:number):T{
return this.data[index];
}
}
const data = new DataManager<number>([1]);
data.getItem(0);
//示例2
interface Item{
name:string;
}
class DataManager<T extends Item>{
constructor(private data:T[]){}
getItem(index:number):string{
return this.data[index].name;
}
}
//const data = new DataManager<number>([1]);
//data.getItem(0);
const data = new DataManager([
{
name:'dell'
}
]);
//示例3
class DataManager<T extends number | string>{
constructor(private data:T[]){}
getItem(index:number):T{
return this.data[index];
}
}
const data = new DataManager<number>([])//number或者string
//如何使用泛型作为一个具体的类型注解
const func:<T>()=>string=<T>()=>{
return '123'
}
function hello<T>(params:T){
return params;
}
const func:<T>(param:T)=>T=hello;
命名空间
使用namespace关键字将一段代码包裹起来,这段被包裹起来的逻辑代码就是一个命名空间。
namespace Computer {
export const PI = 3.14;//前面加export才可以导出 在外部使用
export function sum(first: number, second: number) {
return first + second;
}
}
// 在外部使用命名空间
console.log(Computer.PI);
console.log(Computer.sum(3, 4));
import 对应的模块化
类与接口
类实现一个接口
interface Radio {
switchRadio(trigger: boolean): void;
}
class Car implements Radio {
switchRadio(trigger) {
return 123
}
}
class Cellphone implements Radio {
switchRadio() {
}
}
interface Battery {
checkBatteryStatus(): void;
}
// 要实现多个接口,我们只需要中间用 逗号 隔开即可。
class Cellphone implements Radio, Battery {
switchRadio() {
}
checkBatteryStatus() {
}
}
类型别名 Type Aliases
let sum: (x: number, y: number) => number
const result = sum(1,2)
type PlusType = (x: number, y: number) => number
let sum2: PlusType
// 支持联合类型
type StrOrNumber = string | number
let result2: StrOrNumber = '123'
result2 = 123
// 字符串字面量
type Directions = 'Up' | 'Down' | 'Left' | 'Right'
let toWhere: Directions = 'Up'
交叉类型 Intersection Types
interface IName {
name: string`在这里插入代码片`
}
type IPerson = IName & { age: number }
let person: IPerson = { name: 'hello', age: 12}
声明文件
内置类型
const a: Array<number> = [1,2,3]
// 大家可以看到这个类型,不同的文件中有多处定义,但是它们都是 内部定义的一部分,然后根据不同的版本或者功能合并在了一起,一个interface 或者 类多次定义会合并在一起。这些文件一般都是以 lib 开头,以 d.ts 结尾,告诉大家,我是一个内置对象类型欧
const date: Date = new Date()
const reg = /abc/
// 我们还可以使用一些 build in object,内置对象,比如 Math 与其他全局对象不同的是,Math 不是一个构造器。Math 的所有属性与方法都是静态的。
Math.pow(2,2)
// DOM 和 BOM 标准对象
// document 对象,返回的是一个 HTMLElement
let body: HTMLElement = document.body
// document 上面的query 方法,返回的是一个 nodeList 类型
let allLis = document.querySelectorAll('li')
//当然添加事件也是很重要的一部分,document 上面有 addEventListener 方法,注意这个回调函数,因为类型推断,这里面的 e 事件对象也自动获得了类型,这里是个 mouseEvent 类型,因为点击是一个鼠标事件,现在我们可以方便的使用 e 上面的方法和属性。
document.addEventListener('click', (e) => {
e.preventDefault()
})
Typescript 还提供了一些功能性,帮助性的类型,这些类型,大家在 js 的世界是看不到的,这些类型叫做 utility types,提供一些简洁明快而且非常方便的功能。
// partial,它可以把传入的类型都变成可选
interface IPerson {
name: string
age: number
}
let viking: IPerson = { name: 'viking', age: 20 }
type IPartial = Partial<IPerson>
let viking2: IPartial = { }
// Omit,它返回的类型可以忽略传入类型的某个属性
type IOmit = Omit<IPerson, 'name'>
let viking3: IOmit = { age: 20 }
配置文件
描述文件中的全局类型
泛型中keyof语法的使用
interface Person{
name:string;
age:number;
gender:string;
}
class Teacher{
constructor(private info:Person){}
getlnfo<T extends keyof Person>(key:T):Person[T]{
return this.info[key];
}
}
const teacher = new Teacher({
name:'dell',
age:18,
gender:'male'
})
const test =teacher.getInfo('name')
console.log(test);
Typescript 高级语法
类的装饰器
装饰器本身是一个函数
类装饰器接收的参数是构造函数
装饰器通过@符号来使用
// function testDecorator(flag: boolean) {
// if (flag) {
// return function(constructor: any) {
// constructor.prototype.getName = () => {
// console.log('dell');
// };
// };
// } else {
// return function(constructor: any) {};
// }
// }
// @testDecorator(true)//这个装饰器装饰下面Test这个类
// class Test {}
// const test = new Test();
// (test as any).getName();
//高端写法
function testDecorator() {
return function<T extends new (...args: any[]) => any>(constructor: T) {
return class extends constructor {
name = 'lee';
getName() {
return this.name;
}
};
};
}
const Test = testDecorator()(
class {
name: string;
constructor(name: string) {
this.name = name;
}
}
);
const test = new Test('dell');
console.log(test.getName());
方法装饰器
// 普通方法,target 对应的是类的 prototype
// 静态方法,target 对应的是类的构造函数// @getNameDecorator static getName() {return this.name;}
function getNameDecorator(target: any, key: string, descriptor: PropertyDescriptor) {
// console.log(target, key);
// descriptor.writable = true;//对方法的descriptor属性进行编辑 能否被修改
descriptor.value = function() {
return 'decorator';
};
}
class Test {
name: string;
constructor(name: string) {
this.name = name;
}
//@getNameDecorator
//static getName() {return this.name;}
@getNameDecorator
getName() {
return this.name;
}
}
const test = new Test('dell');
console.log(test.getName());
访问器的装饰器
function visitDecorator(target: any, key: string, descriptor: PropertyDescriptor) {
// descriptor.writable = false;
}
class Test {
private _name: string;
constructor(name: string) {
this._name = name;
}
get name() {//get set 访问器
return this._name;
}
@ visitDecorator
set name(name: string) {
this._name = name;
}
}
const test = new Test('dell');
test.name = 'dell lee';
console.log(test.name);
属性的装饰器
// function nameDecorator(target: any, key: string): any {
// const descriptor: PropertyDescriptor = {
// writable: false
// };
// return descriptor;
// }
// test.name = 'dell lee';
// 修改的并不是实例上的 name, 而是原型上的 name
function nameDecorator(target: any, key: string): any {
target[key] = 'lee';
}
// name 放在实例上
class Test {
@nameDecorator
name = 'Dell';
}
const test = new Test();
console.log((test as any).__proto__.name);
参数装饰器
// 原型,方法名,参数所在的位置
function paramDecorator(target: any, method: string, paramIndex: number) {
console.log(target, method, paramIndex);
}
class Test {
getInfo(name: string, @paramDecorator age: number) {
console.log(name, age);
}
}
const test = new Test();
test.getInfo('Dell', 30);
装饰器实际使用案列
const userInfo: any = undefined;
function catchError(msg: string) {
return function(target: any, key: string, descriptor: PropertyDescriptor) {
const fn = descriptor.value;
descriptor.value = function() {
try {
fn();
} catch (e) {
console.log(msg);
}
};
};
}
class Test {
@catchError('userInfo.name 不存在')
getName() {
return userInfo.name;
}
@catchError('userInfo.age 不存在')
getAge() {
return userInfo.age;
}
@catchError('userInfo.gender 不存在')
getGender() {
return userInfo.gender;
}
}
const test = new Test();
test.getName();
test.getAge();