前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【TypeScript】超详细的笔记式教程!进阶!!【下】

【TypeScript】超详细的笔记式教程!进阶!!【下】

作者头像
HoMeTown
发布2022-10-26 08:33:50
6280
发布2022-10-26 08:33:50
举报
文章被收录于专栏:秃头开发头秃了

类型别名

类型别名顾名思义,即字面意思,其实断言也是字面意思,就是断定类型的方法,你说是什么类型就是什么类型,推翻约定,扯远了,继续说类型别名,举个🌰吧:

代码语言:javascript
复制
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
    if(typeof n === 'string') {
        return n
    } else {
        reutrn n()
    }
}

类型Name其实就是string的别名,类型() => string,一个函数返回一个字符串,这种格式就是类型NameResolverNameOrResolver是一个联合类型,之前说过。

字符串字面量类型

字符串字面量类型,用来约束取值职能是某几个字符串其中的一个字符串,举个🌰:

代码语言:javascript
复制
type EventSupport = 'click' | 'scroll' | 'mouseEnter'
function handleEvent(ele: Element, event: EventSupport): void {
    // do something
}

handleEvent(document.getElementById('app'), 'scroll') // 完全ok
handleEvent(document.getElementById('app'), 'dbclick') // 完全不ok

元组(Tuple)

元组用来合并不同类型的项,举个🌰:

代码语言:javascript
复制
let tom: [string,number] = ['tom', 25]

注意:

  • 元组在定义赋值时,你定义的什么类型,初始赋值时,就得添加什么类型的值,必须全部添加完,不能多,也不能少
  • 可以利用下标修改值,但是值必须是相同类型的
  • 元组可以越界,越界的元素只能是你定义元组的时候的联合类型,不能是其他类型,越界的元素不能修改

.. 元组不是很好用,如果你真的不确定你的[]里有啥,其实最好就用let tom: any[] = ['tom', 12]

枚举(Enum)

枚举一般用来做映射,举个栗子:

代码语言:javascript
复制
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat}

上面这段Ts编译成Js是这样:

代码语言:javascript
复制
var Days;
(function (Days) {
    Days[Days["Sun"] = 0] = "Sun";
    Days[Days["Mon"] = 1] = "Mon";
    Days[Days["Tue"] = 2] = "Tue";
    Days[Days["Wed"] = 3] = "Wed";
    Days[Days["Thu"] = 4] = "Thu";
    Days[Days["Fri"] = 5] = "Fri";
    Days[Days["Sat"] = 6] = "Sat";
})(Days || (Days = {}));

枚举成员会被赋值为从0开始递增的数字,同事也会对枚举值到枚举名进行反向映射:

代码语言:javascript
复制
console.log(Days[0]); // Sun
console.log(Days["Sun"]) // 0

自定义枚举

代码语言:javascript
复制
enum Days {
  Sun = <any>"Sun",
  Mon = <any>"Mon",
  Tue = <any>"Tue",
  Wed = <any>"Wed",
  Thu = <any>"Thu",
  Fri = <any>"Fri",
  Sat = <any>"Sat",
}
console.log(Days["Sun"]); // Sun

注意:这儿需要使用断言,让tsc无视类型检查

其实在JS在ES6发布之前从来都没有类的概念,都是通过构造函数去模拟类,ES6发布了class,但是大部分Js程序员对类的概念还是比较模糊的,这我解释一下什么是类、对象、OOP、封装、集成、多态、修饰符、抽象类、接口

类的概念

其实可以理解为一件事物的抽象,包含了这个事务的一些属性与方法举个简单的🌰,比如人,人就是一个大类,我们可以抽象出来他的一些特点,比如:这是人的行为,智商情商性别等是人的属性。

对象

对象其实就是类的实例化,是一个抽象,对象就是让他变得现实,一个类可以实例化多个对象,类似我们可以根据这个类,制造很多人。

面向对象 OOP

面向对象开发的三大特性:封装、继承、多态

封装

封装的意思就是我们知道的意思,我们需要通过一些代码实现一个函数,这个函数就是一个封装,再通俗一点说,我们需要实现这个方法,细节呢?我们只需要封装起来,比如先下蹲一点,双腿发力,向上用力等等,把这些细节封装起来,就可以直接调用这个方法进行,同时呢外界也不能直接去修改内部的数据。

继承

子承父业,儿子继承老爹的所有方法&属性,但除了拥有父类所有用的特性外,还有一些别具一格的特性。

多态

由于继承产生了很多相关的不同类,很多儿子继承了同一个老爹,子类对同一个方法可以有不同的响应。比如小王小李都继承老爹老张,但是分别实现了自己getMoney的方法,此时针对一个实例,我们无需了解他是小王还是小李,就可以直接调用getMoney方法,程序会自动判断出来应该如何执行getMoney

修饰符

修饰符是一些关键字,用于限定成员或者类型的性质,比如public表示公有属性or方法

抽象类

抽象类是供其他类继承的基类,抽象类不允许被实例化,抽象类中的抽象方法必须在子类中被实现

接口

不同类之间共有的属性和方法,可以抽象成一个接口,接口可以被类实现,一个类职能继承自另外一个类,但是可以实现多个接口。

ES6中类的用法

类的定义

使用class定义类,使用constructor定义构造函数,通过new生成新实例的时候,会自动调用构造函数。

代码语言:javascript
复制
class Person {
    public IQ;
    publick name;
    constructor(IQ, name) {
        this.IQ = IQ
        this.name = name
    }
    saySomething() {
        return `hello, im ${this.name}, IQ: ${this.IQ}`
    }
}
let alice = new Person("Alice", 120)
console.log(alice.saySomething())

类的继承

现在有了人的类,我们现在实现一个Cop类,Cop也属于人,也有名字&IQ,子类中用super关键字来调用父类的构造函数与方法:

代码语言:javascript
复制
class Cop extends Person {
    public job;
    constructor(IQ, name, job) {
        super(IQ, name)
        this.job = job;
    }
    saySomething() {
        return super.saySomething() + 'my job js ${this.job}'
    }
}
let tom = new Cup('tom', 100, 'cop')
console.log(tom.saySomething())

存取器

使用 getter 和 setter可以改变属性的赋值和读取行为:

代码语言:javascript
复制
class FakerCop extends Person {
    constructor(IQ, name, job) {
        super(IQ, name)
        this.job = job
    }
    get job() {
        return 'fakerCop'
    }
    set job(val) {
        console.log('u want my job is' + value + '?')
    }
}
let tony = new FakerCop("Tony", 200, 'fakerCop');
tony.job = 'realCop'
console.log(tony.job) // fakerCop

静态方法

使用static修饰符修饰的方法成为静态方法,不需要被实例化,直接通过类来调用,举个🌰,定义一个判断真假cop的方法:

代码语言:javascript
复制
class Cop extends Person {
    public job;
    constructor(IQ, name, job) {
        super(IQ, name)
        this.job = job;
    }
    static isCop(c) {
        return c instanceof Cop;
    }
    saySomething() {
        return super.saySomething() + 'my job js ${this.job}'
    }
}

console.log(Cop.isCop(tom)) // true
console.log(Cop.isCop(tony)) // false

TypeScript中类的用法

public private protected

  • public 修饰的属性或者方法是公有的,可以在任何地方被访问,默认所有的方法和属性都是public
  • private 修饰的属性或者方法是私有的,不能再声明他的类的外部访问
  • protected 修饰的属性或者方法是受保护的,他和private类似,不同的是,它在子类中是可以访问的

public 举个栗子:

代码语言:javascript
复制
class Animal {
    public name;
    public constructor(name) {
        this.name = name
    }
}

let a = new Animal('Jack')
console.log(a.name) // Jack
a.name = 'Tom'
console.log(a.name) // Tom

private 举个栗子:

代码语言:javascript
复制
class Animal {
    private name
    public constructor(name) {
        this.name = name
    }
}
let a = new Animal('Jack')
console.log(a.name)
a.name = 'Tom'

ts 会提示属性“name”为私有属性,只能在类“Animal”中访问。

子类中也不能访问,这里就不说了;

protected 举个栗子:

代码语言:javascript
复制
class Animal {
    protected name;
    public constructor(name) {
        this.name = name
    }
}
class Cat extends Animal {
    constructor(name) {
        super(name);
        console.log(this.name)
    }
}

子类可以访问父类的protected属性

当构造海曙为protected时,这个类只能继承,不能被实例化:

代码语言:javascript
复制
class Animal {
    protected name;
    protected constructor(name) {
        this.name = name
    }
}

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

let a = new Animal('Jack')

参数属性

修饰符和readonly还可以在构造函数参数中使用,等同于类中定义该属性同时给该属性赋值,代码看上去会更简洁:

代码语言:javascript
复制
class Animal {
    // public name: string;
    public constructor(public name) {
        // this.name = name
    }
}

readonly

只读关键字

代码语言:javascript
复制
class Animal {
    public constructor(readonly name) {
        
    }
}
let a = new Animal('Jack')
console.log(a.name)
a.name = 'Tony' // Error

需要注意的是,如果readonly与其他修饰符同时出现的时候,需要写在最后,改造上面的栗子:

代码语言:javascript
复制
class Animal {
    public constructor(public readonly name: string) {}
}

抽象类

abstract 用于定义抽象类和其中的抽象方法,首先抽象类不允许被实例化,举个栗子:

代码语言:javascript
复制
abstract class Animal {
    public constructor(public name: string) {}
    public abstract sayHi(): void;
}
let a = new Animal('jack')

抛出错误:

其次,抽象类的抽象方法必须被子类实现:

代码语言:javascript
复制
class Cat extends Animal {
    public eat() {
        console.log()
    }
}

如果不实现sayHi方法,就会抛出错误

正确的做法如下:

代码语言:javascript
复制
abstract class Animal {
  public constructor(public name: string) {}
  public abstract sayHi(): void;
}

class Cat extends Animal {
  public eat(): void {
    console.log("im eating");
  }
  public sayHi(): void {
    console.log("hi~ my name is " + this.name);
  }
}

let c = new Cat("Jack");
c.eat();
c.sayHi();

类的类型

很简单

代码语言:javascript
复制
class Animal {
  public constructor(public name: string) {}
  public sayHi(): string {
    return `Hi~ my name is ${this.name}`;
  }
}

let a: Animal = new Animal("Jack");

泛型

泛型是指在定义函数、接口、类的时候,不预先指定具体类型,而在使用的时候再指定类型的一种特性。

基本定义

我们实现一个 createArray函数,他可以创建一个指定长度的数组,同事将每一项都填充一个默认值:

代码语言:javascript
复制
function createArray(length: number, value: any): Array<any> {
    let result = []
    for(let i = 0; i < length; i++) {
        result[i] = value
    }
    return result
}

上面我们用到的是数组泛型,但是any还是有点不妥,我们希望与 value的类型相同,但是我们并不知道value是什么类型,这时候泛型就起作用了:

代码语言:javascript
复制
function createArray<T>(length: number, value: T): Array<T> {
    let result: T[] = []
    for(let i = 0; i < length; i++) {
        result[i] = value
    }
    return result
}
createArray<string>(3, 'x')

上面的栗子里,我们在函数名后面添加了<T>,其中T用来指代任意输入的类型,在后面输入value: T和输出Array<T>中即可使用了。

接下来的调用中,具体的指定它为string,也可以不指定,交给类型推导

代码语言:javascript
复制
createArray(3, 'x')

多个类型参数

定义泛型的时候,可以一次定义多个类型参数:

代码语言:javascript
复制
function swap<T, U>(tup;e: [T, U]): [T, U] {
    return [tuple[1], tuple[0]]
}
swap([7, 'seven']) // ['seven', 7]

泛型约束

函数内部使用泛型变量的时候,我们可能不知道它到底是那种类型,所以不能随意的操作他的属性或者方法:

代码语言:javascript
复制
function loggingIdentity<T>(arg: T): T {
    console.log(arg.length)
    return arg
}

这样写会抛出错误:

因为泛型T上不一定有length,这时候,我们可以对泛型进行约束,只允许这个函数传入那些包含length属性的变量,这就是泛型约束

代码语言:javascript
复制
interface lengthwise {
    length: number;
}
function loggintIdentity<T extends lengthwise>(agr: T): T {
    console.log(agr.length)
    return arg
}

这个时候如果你调用loggintIdentity传入的值没有length属性,那么会抛出错误

多个类型参数之间也可以互相约束:

代码语言:javascript
复制
function copyFields<T extends U, U>(target: T, source: U): T {
  for (const key in source) {
    if (Object.prototype.hasOwnProperty.call(source, key)) {
      target[key] = (<T>source)[key];
    }
  }
  return target;
}
let x = { a: 1, b: 2, c: 3, d: 4 };
console.log(copyFields(x, { b: 10, d: 20 }));

这块可能有同学看不懂target[key] = (<T>source)[key]是啥意思了,我在这解释一下:

(<T>source)[key] === (source as T)[key]

现在的情况是:target的类型是Tsource的类型是U,二者的类型不同,所以无法直接赋值,二者必须断言一个,使他们变成统一类型,如果不这么做,会抛出一个错误:

泛型接口

接口可以约定一个函数的形状:

代码语言:javascript
复制
interface SearchFunc {
    (source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function (source: string, substring: string) {
    return source.search(subString) !== -1
}

也可以使用有泛型的接口定义函数的形状:

代码语言:javascript
复制
interface CreateArrayFunc {
  <T>(length: number, value: T): Array<T>;
}
let createArray: CreateArrayFunc;
createArray = function <T>(length: number, value: T): Array<T> {
  let result: T[] = [];
  for (let i = 0; i < length; i++) {
    result[i] = <T>value;
  }
  return result;
};
console.log(createArray(3, "x"));

泛型参数的默认值

代码语言:javascript
复制
interface CreateArrayFunc {
  <T>(length: number, value: T): Array<T>;
}
let createArray: CreateArrayFunc;
createArray = function <T = string>(length: number, value: T): Array<T> {
  let result: T[] = [];
  for (let i = 0; i < length; i++) {
    result[i] = <T>value;
  }
  return result;
};
console.log(createArray(3, "x"));
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-04-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 秃头开发头秃了 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 类型别名
  • 字符串字面量类型
  • 元组(Tuple)
  • 枚举(Enum)
    • 自定义枚举
      • 类的概念
        • 对象
          • 面向对象 OOP
            • 封装
            • 继承
            • 多态
            • 修饰符
            • 抽象类
            • 接口
          • ES6中类的用法
            • 类的定义
            • 类的继承
            • 存取器
            • 静态方法
          • TypeScript中类的用法
            • public private protected
            • 参数属性
            • readonly
          • 抽象类
            • 类的类型
            • 泛型
              • 基本定义
                • 多个类型参数
                  • 泛型约束
                    • 泛型接口
                      • 泛型参数的默认值
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档