TS入门学习-1

导读:本文章按照TypeScript中文网章节设置,会对官方文档补充一些案例(个人理解学习,仅供参考)欢迎交流。

本章内容:基础类型、变量声明、接口、类、函数、泛型、枚举、类型推论、类型兼容性。

1、基础类型

布尔类型boolean

let isTrue:boolean = true;
let isFalse:boolean = false;

数字类型number  支持十进制,十六进制,二进制,八进制字面量

let num1:number  = 10;
// 十六进制是:0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f
// 其中字母的大小写并不敏感
// 在程序中开头使用0x作为前缀表示十六进制数
let num2:number  = 0xF01d;
// 在程序中开头使用0b作为前缀表示二进制数
let num3:number = 0b0101;
// 在程序中开头使用0o作为前缀表示八进制数,这里八进制的写法会和其他地方略有不同
let num4:number = 0o123;

字符串

// 字符串,双引号和单引号都是支持的
let str:string = "string";
let str1:string = 'string';
//  模板字符串(支持插入变量,换行,和表达式)
let str2 : string = `string${num1}
                     string${num2 + num3}
                     string${num3}`

数组  

// 直接在元素类型后拼接[]
let list1 : number[] = [1,2,3]
// 使用数组泛型
let list2 : Array<number> = [1,2,3]

元组

元素数量已知,元素类型不必一致
let tuple1: [string,number,string] = ['s',1,'a']

enum枚举

enum color{
    red,
    green,
    blue
}
console.log(color[0],color[1],color[2])
// 重写定义数值
enum colorT{
    red = 1,
    green = 4 ,
    blue
}
console.log(colorT[1],colorT[4],colorT[5])

any任意类型

let any1:any = "1111"
// any类型避开类型检查,可以调用任意方法
any1.toString()

void没有类型

// void没有类型
function fn1():void{
    // return 1 不能有返回值
}
let void1 : void 
let null1 : null = null
// let void1 : void = null; 中文文档中说null可以赋值,但是实测不行
console.log(typeof(void1),typeof(null1))//undefined object
let void2 : void = undefined;

null、 undefined、 never

// null undefined
// null 是一个表示“没有值”的特殊关键字。
// 它可以赋值给任何类型,包括原始类型和对象类型,作为它们的默认值。
// null 通常用于初始化一个变量,表示它当前没有指向任何对象。

// undefined 是一个表示变量已声明但尚未被赋值的值。
// 它也是一个原始类型,但与 null 不同,undefined 不能赋值给非原始类型(如对象、数组、函数等)。
// undefined 在JavaScript中是一个常见的值,用于表示属性或变量未被赋值。


// never
// 表示永远不存在的值,任何类型都不是never的子类型,只有never类型可以赋值给never
function error():never{
    throw new Error("cccccccccc")
}
let never1 : never = (()=>{
    return error()
})()
let void3 :void = never1 

object

// object
// object表示非原始类型,也就是除number,string,boolean,symbol,null或undefined之外的类型

类型断言

// 类型断言
// 通过类型断言这种方式可以告诉编译器,“相信我,我知道自己在干什么”。 类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。 它没有运行时的影响,只是在编译阶段起作用。 TypeScript会假设你,程序员,已经进行了必须的检查。
let any2:any = "8888888888888888"
let num5:number = (any2 as string).length;
// 若是我们数据类型,可以参用强制类型转化
let num6:number = any2 as number;
let num7:number = <number>any2;

2、变量声明

var和let的区别

// 函数(var)作用域和块级作用域
function isUseVar(isUse:boolean){
  if(isUse){
    var x = '11';
  }
//   return x;
}

isUseVar(true);//11 正常运行,var不限制于块级作用域
isUseVar(false);//undefined  var声明可以省略,所以x是没有赋值的
// 重定义及屏蔽
// var可以在一个域内重复声明相同的变量(名),且覆盖之前
function f1(){
    var x;
    let y;
    var x;
    // let y;
    if(true){
        var x;
        let y;//与父域中的y不冲突
    }
}
// 变量提升
//var 声明的变量会发生变量提升,这意味着变量可以在声明之前使用,但只会被赋值为 undefined。

let和const的区别

// const 与 let的区别
const c1 = 1;//不能再重新赋值
const c2 = {name:1}
// c2 = {name:2} error
c2.name = 2;//引用类型,const指向的是该类型的内存地址,可以改变它内部的数值

解构数组

// 解构数组
// 赋值
let arr1 = [1,2];
let [first,second] = arr1;
console.log(first);
console.log(second);
// 定义参数类型
function f2([first,second]:[number,number]){
    console.log(first);
    console.log(second);
}
f2([1,3])
// 余量赋值
{
    let [first,...rest] = [1,2,3]
    console.log(first);
    console.log(rest);
}

结构对象

{
    let obj = {name:1,age:2,id:3}
    let {name , age , id } = obj;
    console.log(name)
    console.log(age)
    console.log(id)
}
//属性重命名
{
    let obj = {a:1,b:'bb'}
    let {a:newa,b:newb} = obj
    console.log(newa)
    console.log(newb)
}
//设置属性类型
{
    let obj = {a:1,b:'bb'}
    let {a:newa,b:newb}:{a:number,b:string} = obj
    console.log(newa)
    console.log(newb)
}
// 设置默认值
{
    function f(obj:{a:string,b?:number}){
        let {a,b=1} = obj
        console.log(b)
    }

    f({a:'aa'})
    f({a:'aa',b:2})
}
// 函数声明
{
    function f3({a,b = 2} = {a:1}){
        console.log(a,b)
    }
    f3({a:3})
    f3()
}

展开

//浅拷贝
{
    class C {
        p = 12;
        m() {
        }
      }
      let c = new C();
      let clone = { ...c };
      clone.p; // ok
      //clone.m(); // error!
}

3、接口

接口写法

{
    // interface关键字声明
    interface In1{
        name:string;
    }

    const in1:In1 = {name:"kk"}
}

接口的属性:可选属性,只读属性

// 可选属性
{
    interface In2{
        name?:string;
        age?:number;
    }

    const in2:In2 = {name:"kk"}
}
// 只读属性
{
    interface In3{
        readonly name:string;
    }
    const in3:In3 = {name:'KK'}
    // in3.name = 'aa'
}

接口的属性类型:函数类型,可索引类型、类类型

// 函数类型
{
    interface In5{
        (source:string,subString:string):boolean;
    }

    let in5:In5;

    in5 = function (a,b){
        return true
    }
}
// 可索引类型
{
    interface In5{
        // [索引:索引签名]:索引返回值;
        [index:number]:string;
        [name:string]:string;
    }
    /**
     * 索引签名有两种类型:number,和string
     * 但是签名类型为number的索引返回值类型必须是string
     * 类型的子类
     * 原因:number类型的索引前面实际也是转化为string类型去索引,所以string签名类型返回值要包含number类型的返回值类型
     */
}
// 类类型
{
    interface In6{
        time_:Date;
        setTime(d:Date):string;
    }

    class In6C implements In6{
        time_: Date;
        setTime(d: Date): string {
            throw new Error("Method not implemented.");
        }
        constructor(){
            this.time_ = new Date()
        }
    }
}

接口的继承

// 继承接口
{
    interface In8{
        name:string;
    }
    interface In9 extends In8{
        age:number;
    }

    const c9:In9 = {age:1,name:"a"}

    const c10 = <In9>{}
    console.log(c10.name)
    c10.name = 'a'
    console.log(c10.name)
}

混合类型

// 混合类型
{
    interface In10{
        (index:number):string;
        reset():void;
        id:number;
    }
    function getIn10():In10{
        let in10 = <In10>function(index){
        }
        in10.reset = function(){

        }
        in10.id = 1
        return in10
    }
    let in10 = getIn10()
}

接口继承类

// 接口基础类
//创建了一个接口继承了一个拥有私有或受保护的成员的类时,这个接口类型只能被这个类或其子类所实现
{
    class Cl6{
        private txt:string;
    }

    interface In11 extends Cl6{
        test():any;
    }

    class Cl7 extends Cl6 implements In11{
        test() {
            throw new Error("Method not implemented.");
        }

    }

}

4、类

类写法

 class Animal{
        move(d:number = 0){
            console.log("Animal")
        }
    }

继承

{
    // 超类 
    class Animal{
        move(d:number = 0){
            console.log("Animal")
        }
    }
    // 子类
    class Dog extends Animal{
        bark(){
            console.log("dog")
        }
    }

    const dog = new Dog()

    dog.move()
}

super关键字

// super
//必须调用 super(),它会执行基类的构造函数。 而且,在构造函数里访问 this的属性之前,我们 一定要调用 super()。 这个是TypeScript强制执行的一条重要规则。
{
  class Animal{
    constructor( private name:string){
    }
  }

  class Snake extends Animal{
    constructor (private id:string){
        super("name")
    }
  }

  class Cat extends Animal{
    constructor(name:string){
        super(name)
    }
  }
}

关键字:public、protected、private、readonly

// public.private.protected
{
    class Animal{
        public name:string;
        private age:number = 1;
        protected id:number = 1;

        constructor(name:string){
            this.name = name;
        }
    }

    class Dunk extends Animal{
        constructor(name:string){
            super(name)
        }
    }
}
// protected可以用于构造方法,只能被继承不能实例化
{
    class Animal{
        protected constructor(name:string){

        }
    }

    class Dog extends Animal{
        constructor(name:string){
            super(name)
        }
    }

    // new Animal("nnnn")无法实例化
}

存取器

// 存取器
{
    class Animal{
        private _name:string;
        constructor(name:string){
            this._name = name;
        }

        set name(name:string){
            this._name = name
        }

        get name():string{
            return this._name;
        }
        
    }

    const cat = new Animal("dog");
    cat.name = 'cat';
    console.log(cat.name);
}

静态属性:学习静态属性后,大家就应该清晰类中的静态属性和实例化属性的区别

// 静态属性
{
    class Animal{
        static live = {name:'animal',cell:'max'};

        getNewAnimal(live:{name:string,cell:string}){
            let newName = live.name + Animal.live.name;
            let newCell = live.cell + Animal.live.cell;
            return {name:newName,cell:newCell};
        }
    }

    const newAnimal = new Animal();
    console.log(newAnimal.getNewAnimal({name:"lonng",cell:"38X"}))
}

抽象类

//抽象类
//抽象类做为其它派生类的基类使用。 它们一般不会直接被实例化。 不同于接口,抽象类可以包含成员的实现细节。 abstract关键字是用于定义抽象类和在抽象类内部定义抽象方法
{
    abstract class Animal{
        abstract sound():void;
    }
}

构造函数

// 构造函数
{
    class Zoo{
        //定义一个静态属性
        static desc = "hello zoo";
        // 需要实例化的属性
        stick:number = 1;
        // 
        walk(){
            if(this.stick>0){
                return "zoo stick"+this.stick;
            }else{
                return Zoo.desc;
            }
        }
    }
    // 按照常用的方式实例化
    const zoo1 = new Zoo();
    zoo1.stick = 100;
    console.log(zoo1.walk())
    // 我们通过赋予构造函数类型
    let zoo2 = Zoo;
    // zoo2.stick = 100 类型“typeof Zoo”上不存在属性“stick"
    // 所以zoo2只有Zoo的静态属性,但是并没有stick可以实例化的属性
    console.log("zoo2.desc",zoo2.desc)
    
    let zoo3:typeof Zoo = Zoo;
    zoo3.desc = "hello zoo3";
    console.log("zoo3.desc",zoo3.desc)

}
// 把类当作接口使用
{
    class Point {
        x: number;
        y: number;
    }
    
    interface Point3d extends Point {
        z: number;
    }
    
    let point3d: Point3d = {x: 1, y: 2, z: 3};

    let point2d:Point = {x:1,y:2}
}

5、函数

函数写法

// 函数
function add(x:number,y:number):number{
    return ( x + y )
}

匿名函数

// 匿名函数
let add2  = function (x:number,y:number):number{
    return x+y;
}

函数的类型

//函数的类型定义
let add2Pro:(xValue:number,yValue:number)=>number = function (x:number,y:number):number{
    return x+y;
}

类型推断

// 类型推断
let add2Plus:(xValue:number,yValue:number)=>number = function(x,y){
    return x+y;
}

可选参数/默认参数

// 可选参数和默认参数
{
    // 我们设置一个起名函数,第一个是姓,第二个和第三个是名(可能是两个字,也会是一个字)
    //可选参数必须跟在必须参数后面
    function setName(first:string,second:string,third?:string):string{
        return first + second + (third ? third : '');
    }
    console.log(setName('xue','jin'));
    // 这里我们默认姓为xue
    //在所有必须参数后面的带默认初始化的参数都是可选的,与可选参数一样,在调用函数的时候可以省略
    //与普通可选参数不同的是,带默认值的参数不需要放在必须参数的后面。 如果带默认值的参数出现在必须参数前面,用户必须明确的传入 undefined值来获得默认值。
    function setName2(second:string,third?:string,first = 'xue'):string{
        return first + second + (third ? third : '');
    }
    console.log(setName2('jin'));
}

剩余参数

//剩余参数
// 说到剩余参数就能想到js中的arguments,看看和ts有什么不一样
{
    function setName3(first:string = 'xue',...rest:string[]){
        return first + rest.join('');
    }
    console.log(setName3(undefined,'jin','chi'));
}

this

{
    let dog = {
        name:['二哈','博美'],
        getName: function (){
            // return function(x:number){
            //     return this.name[x]
            // }
            // 'this' implicitly has type 'any' because it does not have a type annotation.
            return (x:number)=>{
               return this.name[x]
            }
        }
    }
    let bomei = dog.getName();
    console.log("=======",bomei(1))
}

this参数

{
    interface Dog{
        name:string[];
        getName(this: Dog):(x:number)=>string;
    }
    let dog:Dog = {
        name:['二哈','博美'],
        getName: function (this:Dog){
            // return function(x:number){
            //     return this.name[x]
            // }
            // 'this' implicitly has type 'any' because it does not have a type annotation.
            return (x:number)=>{
               return this.name[x]
            }
        }
    }
    let bomei = dog.getName();
    console.log("=======",bomei(1))
}

重载

{
    enum Suit{
        "hearts",
        "spades",
        "clubs",
        "diamonds"
    }

    type Card = {
        suit:Suit,
        card:number
    }
    function pickCard(x:Card[]):number;
    function pickCard(x:number):Card;

    function pickCard(x:any):any{
        if(typeof x == 'object'){
            return Suit[x.suit]
        }else if(typeof x == 'number'){
            let pickCard = Math.floor(x / 13)
            return  {suit:pickCard,card: x % 13}
        }
    }

    const cards:Card[] = [{suit:Suit['hearts'],card:2}]
    let p1 = pickCard(cards)
    console.log(p1)
    let p2 = pickCard(15)
    console.log(p2.card,Suit[p2.suit])
}

6、泛型

泛型的定义

泛型提供了一种方式,使得函数、接口或类能够接收一个或多个类型参数,这些参数在定义时不需要指定,而是在使用时确定。

function fn<T>(x:T):T{
        return x
    }

泛型变量

 function t2<T>(x:T[]):T[]{
        return x
    }

    function t3<T>(x:Array<T>):T[]{
        return x
    }

泛型类型

function t4<T>(arg:T):T{
        return arg;
    }

    let t4_: <T>(arg:T)=>T =t4;
    // 对象字面量
    let t4_2: {<T>(arg:T):T} = t4;
    // 再变为接口类型
    interface T4{
        <T>(arg:T):T;
    }
    let t4_3 : T4 = t4;

    // 
    interface T4_<T>{
        (arg:T):T;
    }

    let t4_4 :T4_<number> = t4;
}

泛型类

/ 泛型类
//类有两部分:静态部分和实例部分。 泛型类指的是实例部分的类型,所以类的静态属性不能使用这个泛型类型。
{
    class T5<T>{
        title:T;
        add: (x:T,y:T)=>T;
    }

    let t5 = new T5<number>();
}

泛型约束


    interface limitT{
        length:number;
    }

    function getLength<T extends limitT>(x:T):number{
        return x.length;
    }

在泛型使用类类型

 function t7<T>(c : {new():T}){
            return new c()
    }

7、枚举

数字枚举

{
    enum e1{
        a = 1,
        b,
        c,
        d
    }
}

字符串枚举

{
    enum e2{
        a = "a",
        b= "b",
        c= "c",
        d= "d",
    }
}

异构枚举

---------文档中不建议使用

计算的和常量成员

// 一个枚举表达式字面量(主要是字符串字面量或数字字面量)
// 一个对之前定义的常量枚举成员的引用(可以是在不同的枚举类型中定义的)
// 带括号的常量枚举表达式
// 一元运算符 +, -, ~其中之一应用在了常量枚举表达式
// 常量枚举表达式做为二元运算符 +, -, *, /, %, <<, >>, >>>, &, |, ^的操作对象。 若常数枚举表达式求值后为 NaN或 Infinity,则会在编译阶段报错。

运行时的枚举,枚举是在运行时真正存在的对象

enum e3{
        X,Y,Z
    }
    function f2(obj:{X:number}){
        return obj.X
    }
    f2(e3)

双向映射

 enum Enum{
        A
    }
    let a = Enum.A
    let A1 = Enum[a]

8、类型推论

类型推论是指编译器根据变量的初始化值自动推断其类型的过程。类型推论可以减少代码中显式类型声明的需求,使代码更加简洁。

9、类型兼容性

// 引用类型的兼容型,比较结构/属性
// 函数 比较参数和返回值
// 函数参数双向协变
{
    enum EventType { Mouse, Keyboard }

interface Event { timestamp: number; }
interface MouseEvent extends Event { x: number; y: number }
interface KeyEvent extends Event { keyCode: number }

function listenEvent(eventType: EventType, handler: (n: Event) => void) {
    /* ... */
}

// Unsound, but useful and common
listenEvent(EventType.Mouse, (e: Event) => console.log((<MouseEvent>e).x + ',' + (<MouseEvent>e).y));

// Undesirable alternatives in presence of soundness
listenEvent(EventType.Mouse, (e: Event) => console.log((<MouseEvent>e).x + ',' + (<MouseEvent>e).y));
listenEvent(EventType.Mouse, <(e: Event) => void>((e: MouseEvent) => console.log(e.x + ',' + e.y)));

// Still disallowed (clear error). Type safety enforced for wholly incompatible types
// listenEvent(EventType.Mouse, (e: number) => console.log(e));
}
//可选参数及剩余参数
{
    function invokeLater(args: any[], callback: (...args: any[]) => void) {
        /* ... Invoke callback with 'args' ... */
    }
    
    // Unsound - invokeLater "might" provide any number of arguments
    invokeLater([1, 2], (x, y) => console.log(x + ', ' + y));
    
    // Confusing (x and y are actually required) and undiscoverable
    invokeLater([1, 2], (x?, y?) => console.log(x + ', ' + y));
}
// 函数重载
// 枚举
{
    enum Status { Ready, Waiting };
    enum Color { Red, Blue, Green };

    let status = Status.Ready;
    // status = Color.Green;  // Error
    status = 4;
}
// 类
// 类与对象字面量和接口差不多,但有一点不同:类有静态部分和实例部分的类型。 比较两个类类型的对象时,只有实例的成员会被比较。 静态成员和构造函数不在比较的范围内。
// 类的私有成员和受保护成员
// 类的私有成员和受保护成员会影响兼容性。 当检查类实例的兼容时,如果目标类型包含一个私有成员,那么源类型必须包含来自同一个类的这个私有成员。 同样地,这条规则也适用于包含受保护成员实例的类型检查。 这允许子类赋值给父类,但是不能赋值给其它有同样类型的类。
{
    class Animal {
        feet: number;
        constructor(name: string, numFeet: number) { }
    }
    
    class Size {
        feet: number;
        constructor(numFeet: number) { }
    }
    
    let a: Animal;
    let s: Size;
    
    // a = s;  // OK
    // s = a;  // OK
}
// 泛型
{
    interface Empty<T> {
    }
    let x: Empty<number>;
    let y: Empty<string>;
    
    // x = y;  // OK, because y matches structure of x
}
{
    interface NotEmpty<T> {
        data: T;
    }
    let x: NotEmpty<number>;
    let y: NotEmpty<string>;
    
    // x = y;  // Error, because x and y are not compatible
}
{
    let identity = function<T>(x: T): T {
        // ...
        return x
    }
    
    let reverse = function<U>(y: U): U {
        // ...
        return y
    }
    
    identity = reverse;  // OK, because (x: any) => any matches (y: any) => any
}
// 子类型与赋值
// 目前为止,我们使用了“兼容性”,它在语言规范里没有定义。 在TypeScript里,有两种兼容性:子类型和赋值。 它们的不同点在于,赋值扩展了子类型兼容性,增加了一些规则,允许和any来回赋值,以及enum和对应数字值之间的来回赋值。

// 语言里的不同地方分别使用了它们之中的机制。 实际上,类型兼容性是由赋值兼容性来控制的,即使在implements和extends语句也不例外。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值