TS入门

118 阅读8分钟

如果代码中有export import 之类的代码,那这个文件会变成一个模块

export {};
let name:strting = 'yzl';

基础类型定义

字符串数组

let hobbies:string[] = ['1','2','3'];
let str:Array<string> = ['4','5','6'];

元组

元组 类似一个数组它是一个长度和类型都固定的数组

let point:[number,number] = [100,100];
let person:[string,number] = ["yzl",100];

类数组

function sum(...arg:any):void {
    let args: IArguments = arguments;
    return Array.from(args).reduce((res,item)=>res+item)
}

console.log(sum(1,2,3,4,5));

枚举类型(enum)

枚举的值不可以是表达式比如

enum Week {
    MONDAY = new Date()
}

普通枚举

枚举就是自己关联自己

enum Gender {
    boy,
    girl
}
console.log(Gender.boy);

转换了之后

var Gender;
(function (Gender) {
    Gender[Gender["boy"] = 0] = "boy";
    Gender[Gender["girl"] = 1] = "girl";
})(Gender || (Gender = {}));
console.log(Gender.boy);

常数枚举

const enum Gender {
    boy,
    girl
}
console.log(Gender.boy);

转换了之后 直接变成常数

console.log(0 /* boy */);

null 和 undefined 是其他类型的子类

nerver 永远不可能出现的值(就是报错或者死递归)

function createError(message:string):never {
	console.log(1);
	throw new Error("error");
	console.log(2);
}

猜类型和推论类型

let num:string | number;
num = 'hellow';
num = 2;

断言

let num:string | number;
num = 'hellow';
num = 2;

//把num断言成number类型
console.log((num as number).toFixed(2));

字面量类型

let Gender:'Boy' | 'GIRL';
Gender = 'Boy';
Gender = 'GIRL';

函数定义

// type 用来定义一个类型或者类型别名字
type G = (name:string)=>void
// 函数定义
let t:G =  function (name:string):void {
    console.log(`你好`+ name);
}

t('yzl');

可选参数

function print(name:string,age?:number,home?:string){
	
}
print('yzl');
print('yzl',21);
print('yzl',21,'温州');
print();

剩余参数

function sum(...res:Array<number>){
	res.reduce((accu,item)=>accu + item,0);
}

重载

function sum(x:number,y:number):void;
function sum(x:string,y:string):void;
function sum(x:any,y:any){
	console.log(x,y)
}

sum(10,20);
sum('10','20');

类中的get方法

class Person {
    age:number;
    constructor (){
        this.age = 10;
    }

    //这个东西算是属性
    get getAge(){
        return this.age;
    }
    //这个东西算是属性
    set setName(val:string){
        this.name = val;
        console.log(1)
    }
}

console.log(new Person)

使用public公开形参变为实例属性

namespace b {
    class Person {
        constructor(public name:string){

        }
    }

    let p1 = new Person("尤子龙");
    console.log(p1.name)
}

继承

//父类
class Person {
    name:string;
    getName():void{
        console.log(this.name);
    }
    constructor(name:string){
        this.name = name;
    }
}

//子类
class Son extends Person {
    constructor(){
        super('尤子龙');
    }
}


/* let p = new Person();
p.getName(); */

let S = new Son();
S.getName();

装饰器

类装饰器

修饰类 target 指向 类本身Person

// 类装饰器
namespace a {
    //这个会和 Person 合并
    interface Person {
        name:string
    }

    function decorator(target:any):void{
        target.name = 'yzl'
    }
    
    @decorator
    class Person {
        constructor(){
            
        }
    } 

    console.log(new Person().name)
}

属性修饰器

普通属性

如果给属性添加修饰器他会有两个参数

  • target 指向类的原型 既 Person.prototype
  • name 是属性的名字
namespace a {
    function decorator(target:any,name:string){
        target.name = '12312'
    }
    
    class Person {
        @decorator
        name:string = 'zhufeng';
        constructor(){
            
        }
    } 

    console.log(new Person().name)
}

静态属性

如果给属性添加修饰器他会有两个参数

  • target 指向类的原型 既 Person
  • name 是属性的名字
namespace a {
    function decorator(target:any,name:string){
        target.name = '12312'
    }
    
    class Person {
        @decorator
        static name1:string = 'zhufeng';
        constructor(){
            
        }
    } 

    console.log(Person.name1)
}

方法修饰器

方法修饰器接受三个参数

  • target 指向原型对象
  • name 方法名字
  • propertyDecorator 属性描述
    • value 是一个函数
namespace a {
    function decorator(target: any, name: string, propertyDecorator: PropertyDescriptor) {
        // 这里是旧方法
        let oldMethod = target[name];
        //重写value
        propertyDecorator.value = function (...args:any[]) {
            let res = args.map(v=> parseFloat(v));
            return oldMethod(...res);
        }
    }

    class Person {
        static name1: string = 'zhufeng';
        @decorator
        sum(...arg: any[]): any {
            return arg.reduce((res, item) => res + item);
        }
    }

    console.log(new Person().sum("1", 2, 3, 4))
}

参数修饰器

参数修饰器接受三个参数

  • target 指向原型对象
  • name 函数名称
  • paramsIndex 参数下标
namespace a {
    function addage(target: any, name: string, index: Number) {
        console.log(target,name,index);
    }

    class Person {
        getData(@addage kkk: any):void {
            
        }
    }

    console.log(new Person().getData(123))
}

修饰器的执行顺序

分析

//方法装饰器
function logmethod(params:any){
    return function(target:any,methodName:any,desc:any){
      console.log(target); // HTTP 
      console.log(methodName); //getData
      console.log(desc);
      // 1保存当前方法
        var oldMethod = desc.value;
        desc.value = function(...args:any[]){//用三点运算符来接收传入的数组参数
            args = args.map((value)=>{
                return String(value);
            })  
            console.log(args);
            oldMethod.apply(this,args);   
        }
     
    }
}
 
class HttpClient{
    public apiUrl:string | undefined;
    constructor(){
      
    }
 
    @logmethod('www.baidu.com')
    getData(...args:any[]){
      console.log(args);
      console.log("我是getData里面的方法");
    }
}
 
var http = new HttpClient();
http.getData("123","xxx");
 

总结:

属性方法先执行,谁先写谁先执行

方法执行的时候,先参数再方法,而且他们一定会在一起

最后是类

如果是同类型,先执行后写

接口

记住一句话 接口只声明不定义

抽象类 abstract

抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。 抽象方法的语法与接口方法相似。 两者都是定义方法签名但不包含方法体。 然而,抽象方法必须包含 abstract关键字并且可以包含访问修饰符。

抽象类可以只声明 但是不定义

abstract class Department {
    constructor(public name: string) {
    }

    printName(): void {
        console.log('Department name: ' + this.name);
    }

    abstract printMeeting(): void; // 必须在派生类中实现
}

class AccountingDepartment extends Department {
    constructor() {
        super('Accounting and Auditing'); // 在派生类的构造函数中必须调用 super()
    }

    printMeeting(): void {
        console.log('The Accounting Department meets each Monday at 10am.');
    }

    generateReports(): void {
        console.log('Generating accounting reports...');
    }
}

let department: Department; // 允许创建一个对抽象类型的引用
// department = new Department(); // 错误: 不能创建一个抽象类的实例
department = new AccountingDepartment(); // 允许对一个抽象子类进行实例化和赋值
department.printName();
department.printMeeting();
// department.generateReports(); // 错误: 方法在声明的抽象类中不存在

可以用来描述对象,对象有什么类型

interface point {
    name:string,
    age:Number
}

let obj:point = {name:'yzl',age:18};

可以用来描述行为(方法)抽象

interface a {
    speak():void
}

interface b {
    eat():void
}

class person implements a,b {
    speak(){
        console.log("a.speak")
    }
    eat(){
        console.log("b.eat")
    }
}

new person().eat()

重写方法

// 动物类
class zoon {
    speak():void{
        console.log("嗷嗷叫");
    }
}

class cat extends zoon {
    speak():void{
        console.log("喵喵喵");
    }
}

new cat().speak();

任意属性

// 任意属性
namespace a {
    interface constraint {
        [propName:string]:string | number
    }

    let obj:constraint = {
        name:'尤子龙',
        age:20
    }
}

接口之间的继承

interface a{
    A():void;
}

interface b {
    B():void;
}

class person implements b,a {
    A(){};
    B(){}
}
//接口之间的继承
interface a{
    A():void;
}

interface b extends a{
    B():void;
}

class person implements b {
    A(){};
    B(){}
}

只读接口

// 只读接口
namespace a {
    interface circleAble {
        readonly PI: number;
        Rs: number
    }
    
    let obj: circleAble = {
        PI : 3.14,
        Rs : 10
    }

    console.log(obj.PI) 
    console.log(obj.PI = 4) // 错误
}

接口约束函数

namespace  a{
   interface a {
       (x:number,y:string):void;
   }

   let fn:a = function (x:number,y:string):void {
        console.log(`${y}今年${x}岁了`);
   }

   fn(20,"尤子龙");
}

约束数组

// 约束 数组或者对象
interface a {
    [index:number]:string
}

// let arr:a = ['1',2];//报错
// 数组
let arr:a = ['1','2'];
// 对象
let arr: a = {
    0: '1',
    1: '2'
};

泛型

就是说 定义函数的时候 T是什么类型我不知道 , 我在调用函数的时候再给他指定

namespace a {
    function createArr<T>(age:T):void {
        console.log(age);
    }

    createArr<number>(20);
}

函数接口泛型

// 接口泛型
interface a {
    <T>(name:T,age:number):void;
}

let show:a = function <T>(name:T,age:number):void {
    console.log(name,age);
}

show("尤子龙",21);

在函数中使用泛型的时候由于预先不知道具体的类型,所以不能访问到相应类型的方法

namespace a {
    // 在函数中使用泛型的时候由于预先不知道具体的类型,所以不能访问到相应类型的方法
    function name<T>(params:T) {
        console.log(params.age);
    }

    name<object>({age:12})
}

type VS interface

interface 定义一个实实在在的接口,它才是一个真正的类型

type 一般用来定义别名并不是真正的类型

相同点

  • 都可以描述一个对象或者函数
  • 都允许拓展(extends)

不同点

  • type 可以声明基本类型别名,联合类型,元组等类型
  • type 语句中还可以使用 typeof 获取实例的 类型进行赋值
  • interface 能够声明合并

interface

interface User {
  name: string
  age: number
}

interface SetUser {
  (name: string, age: number): void;
}

type

type User = {
  name: string
  age: number
};

type SetUser = (name: string, age: number)=> void;

都允许拓展(extends)

interface 和 type 都可以拓展,并且两者并不是相互独立的,也就是说 interface 可以 extends type, type 也可以 extends interface 。 虽然效果差不多,但是两者语法不同。

interface extends interface

interface Name { 
  name: string; 
}
interface User extends Name { 
  age: number; 
}
123456

type extends type

type Name = { 
  name: string; 
}
type User = Name & { age: number  };
1234

interface extends type

type Name = { 
  name: string; 
}
interface User extends Name { 
  age: number; 
}
123456

type extends interface

interface Name { 
  name: string; 
}
type User = Name & { 
  age: number; 
}
123456

不同点

type 可以而 interface 不行

type 可以声明基本类型别名,联合类型,元组等类型

// 基本类型别名
type Name = string

// 联合类型
interface Dog {
    wong();
}
interface Cat {
    miao();
}

type Pet = Dog | Cat

// 具体定义数组每个位置的类型
type PetList = [Dog, Pet]
123456789101112131415

type 语句中还可以使用 typeof 获取实例的 类型进行赋值

// 当你想获取一个变量的类型时,使用 typeof
let div = document.createElement('div');
type B = typeof div
123

其他骚操作

type StringOrNumber = string | number;  
type Text = string | { text: string };  
type NameLookup = Dictionary<string, Person>;  
type Callback<T> = (data: T) => void;  
type Pair<T> = [T, T];  
type Coordinates = Pair<number>;  
type Tree<T> = T | { left: Tree<T>, right: Tree<T> };
1234567

interface 可以而 type 不行

interface 能够声明合并

interface User {
  name: string
  age: number
}

interface User {
  sex: string
}

/*
User 接口为 {
  name: string
  age: number
  sex: string 
}
*/
12345678910111213141516

总结

一般来说,如果不清楚什么时候用interface/type,能用 interface 实现,就用 interface , 如果不能就用 type 。

super

  • super一种是在构造函数中给父类的构造函数传参
  • 一种是代表父类的原型对象

第一种情况

// 动物类
class zoon {
    constructor(public name:string){
        this.speak()
    }
    speak():void{
        console.log(this.name + "嗷嗷叫");
    }
}

class cat extends zoon {
    constructor(name:string){
        super(name);
    }
}

new cat('尤子龙').speak();

第二种情况

// 动物类
class zoon {
    constructor(public name:string){
        this.speak()
    }
    speak():void{
        console.log(this.name + "嗷嗷叫");
    }
}

class cat extends zoon {
    constructor(name:string){
        super(name);
    }

    callSpeak(){
        super.speak()
    }
}

new cat('尤子龙').callSpeak();