typescript基础

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();

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值