首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >TypeScript基础知识

TypeScript基础知识

作者头像
Clearlove
发布2021-03-04 10:46:06
6450
发布2021-03-04 10:46:06
举报
文章被收录于专栏:前端客栈前端客栈

基本数据类型

数字
const a: number = 1;
字符串
const b: string = '1';
数组
const c: number[] = [1, 2, 3];
const d: Array<number> = [1, 2, 3];
const e: any[] = [1, '2', true];
布尔
const f: boolean = true;
对象
const g: object = {};
undefined

常用于组合类型

const h: number | undefined;
null
const i: null;
元组

可为数组中的每个参数定义相对应的类型

const j: [number, string] = [1, '2'];
枚举
enum err {
  first = 3,
  'second',
}

const k: e = err.first;
console.log(g); // 4

tips

  1. 如果未赋值的上一个值是数字,那么这个未赋值的值就是上一个值 +1
  2. 如果未赋值的上一个值未赋值,那么输出的就是它的下标
  3. 如果未赋值的上一个值是非数字,那么必须赋值
void

指定方法类型,表示没有返回值,方法体中不能有return

function add(): void {
  console.log('add');
}

// 如果方法体有返回值,可以加上返回值的类型
function delete: string {
  return 'delete';
}
never

其他类型(包括undefind和null)的子类型,代表从不会出现的值

let o: never;

o = (() => {
  throw new Error('error msg');
})();
任意类型

让参数可以是任何一种类型

let p: any = 1;

p = '2';
p = true;

函数

函数声明
function add(): vide {}
方法参数
function getUserInfo(name: string, age?: number, school: string = '哈佛大学'): string {
  return `name: ${name}, age: ${age || '年龄不详'}, school: ${string}`;
}

tips ?代表这个参数可不传,不传即为undefined,也可定义默认值

剩余参数
function sum(a: number, b: number, ...arr: number[]): number {
  let sum: number = a + b;
  arr.forEach(i => {
    sum += i;
  });
  
  console.log(arr); // [3, 4, 5]
  return sum;
}

console.log(sum(1, 2, 3, 4, 5)); // 15
函数重载
不同的数据类型
function reload(name: string): void {}
function reload(age: number): void {}
function reload(info: any): any {
  if (typeof(info) === 'string') {
    console.log(`我的名字是: ${info}`);
  } else if (typeof(info) === 'number') {
    console.log(`我的年龄是: ${info}`);
  }
}

reload('Clearlove'); // 我的名字是Clearlove
reload(18); // 我的年龄是18
不同的参数
function reload(name: string): void
function reload(name: string, age?: number): void
function reload(name: any, age? number): any {
  if (age) {
    console.log(`我的名字是: ${name}, 今年${age}岁!!`);
  } else {
    console.log(`大家好,我的名字是: ${name}`);
  }
}

reload('Clearlove'); // 大家好,我的名字是Clearlove
reload('Clearlove', 18); // 我的名字是Clearlove, 今年18岁!!

tips

  1. 被重载的函数,是没有函数体的,可以根据参数的类型走其中一个方法并判断参数
  2. 函数的重载与返回值类型无关
  3. 函数重载的作用:是一种参数校验功能,在进行函数调用时,会对参数进行检查,只有传人的参数类型、顺序、个数和重载的函数的参数相同时,才能调用成功,否则报错

class Person {
  // 私有变量
  private name: string;
  
  // 构造函数
  constructor(name: string) {
    this.name = name;
  }
  
  getName(): string {
    return this.name;
  }
  
  setName(name: sring): void {
    this.name = name;
  }
}

const myBoy = new Person('Clearlove');
console.log(myBoy.getName()); // Clearlove
myBoy.setName('test');

继承

class Son extends Person {
  // 静态属性
  public static age: number = 18;
  
  // 学校
  public school: string;
  
  constructor(name: string, school: string) {
    // 访问派生类的构造函数前,必须调用“super”,初始化父类构造函数,并把参数传给父类
    super(name);
    this.school = school;
  }
  
  // 静态方法
  static run(name: string): string {
    return `${name}在跑步,他是年龄是${this.age}`;
  }
}

const son = new Son('Clearlove', '清华大学');
son.setName('Test');
console.log(son);
console.log(Son.run('Clearlove')); // Clearlove在跑步,他的年龄是18
console.log(Son.age); // 18

tips

  1. public 在当前类、子类和类以外都可以访问
  2. protected 在当前类、子类内部都可以访问,类外部无法访问
  3. private 在当前类内部可以访问,子类和类外部无法访问
  4. 属性不加修饰符,默认都是public

多态

通过抽象方法/方法重载,实现多态。多态的作用是用来定义标准 // 抽象父类

abstract class Animal {
  // 私有属性
  private name: string;
  constructor(name: string) {
    this.name = name;
  }
  // 抽象成员: 方法
  abstract eat(): any;
  // 抽象成员: 属性
  protected abstract ages: number; 
  sleep(): void {
    console.log(`${this.name}在睡觉`);
  }
}

class Cat extends Animal {
  ages: number = 2;
  constructor(name: string) {
    super(name);
  }
  // 非抽象类: Cat 不会自动实现继承自: Animal类的抽象方法: eat, 必须手动定义父类中的抽象方法,着就是多态
  eat(): string {
    return '猫吃鱼';
  }
  sleep(): string {
    return '猫在睡觉';
  }
}

const cat = new Cat('Tom');
cat.sleep();

tips

  1. 抽象类无法 实例化
  2. 非抽象类继承父类时,不会自动实现 来自父类的抽象成员,必须手动定义 父类中的成员,否则会报错
  3. 抽象成员包含 属性方法

接口

在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范。 在程序设计里面,接口起到了一种限制和规范的作用。 接口定义了某一批类所需遵守的规范,接口不必关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这批类必须提供某些方法,提供这些方法的类就可以满足实际需要。ts中的接口类似java,同时还增加了更灵活的接口类型,包括属性、函数、可索引和类等。

属性接口
interface InterfaceName {
  first: string;
  second?: string;
}

function logParam(name: InterfaceName): viod {
  console.log(name.first, name.second. 'test');
}

const obj = { first: '1', second: '2'. three: '3' };
logParam({ first: '1', second: '2'. three: '3' }); // 报错,只能传接口定义的值
logParam(obj);

tips 用变量存储数据,这样可以传入定义的接口外的值,否则如果直接传入对象中无接口定义的值会报错

#### 函数类型接口 对函数传入的参数类型,以及返回值类型进行约束,可批量进行约束

interface keyMap {
  (key: string, value: string): string;
}

let logKeyMap: keyMap = fucntion (key: string, value: string): string {
  return key + value;
}

console.log(logKeyMao('key', 'value'));

tips 接口只对传入的参数的类型和参数的个数进行约束,不对参数名称进行约束

可索引接口
  • 约束数组
interface Arr {
  [index: number]: string;
}
let test: Arr = ['123'];
  • 约束对象
interface Obj {
  [index: string]: string;
}

let test: Obj = { name: 'Clearlove' };

tips

  1. 数组 进行约束,index必须是 number类型
  2. 对象 进行约束,index必须是 string类型
  3. 索引签名参数类型必须为string或者number
类类型接口
  • 进行约束,类似 抽象类 的实现
interface Ainmal {
  name: string;
  eat(): void;
}

calss Dogs implements Animal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  eat() {}
}
  • 接口继承(接口可以继承接口)
interface Dog {
  ear(): void;
}

interface Persons entexds Dog {
  work(): void;
}

class Cat {
  code() {
    console.log('猫在敲代码');
  }
}

class SuperMan extends Cat implements Persons {
  eat(): void {
    console.log('eat');
  }
  work(): void {
    console.log('work');
  }
}

const man = new SuperMan();
man.code();

tips 类接口会对类的 属性方法进行约束,类似非抽象类继承类时必须实现某些方法和属性,但对于属性和方法的类型约束更加严格。除了方法 void类型 可被 重新定义外,其他属性或方法的类型定义需要和接口保持一致。

泛型

软件工程中,我们不仅要创建一致的、定义良好的API, 同时也要考虑重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,在创建大型系统时为你提供了十分灵活的功能。 泛型就是解决 接口方法复用性,以及对不特定数据类型的支持。 要求:传入的参数和返回的参数一致

函数的泛型
function getDate<T>(value: T): T {
  return value;
}
const val = getDate<number>(123);
console.log(val);

tips 这里的T 可以改成其他任意值,但定义的值和传入的参数以及返回的值是一样的。一般默认写法是T,也是业内规范的选择。

类的泛型
class MainClass<T> {
  public list: T[] = [];
  add(value: T): void {
    this.list.push(value);
  }
  min(): T {
    let minNum = this.list[0];
    for(let i = 0; i < this.list.length; i++) {
      minNum < this.list[i] ? minNum : this.list[i];
    }
    return minNum;
  }
}

// 实例化类,指定类的T的类型是number
const minClass = new MainClass<number>();
minClass.add(1);
minClass.add(2);
minClass.add(3);
console.log(minClass.min());
// 实例化类,并指定了类的T的类型是string,则其方法的传参和返回值都是string类型
let minClass2 = new MainClass<string>();
minClass2.add('1');
minClass2.add('2');
minClass2.add('3');
console.log(minClass2.min());
接口的泛型
  • 第一种写法
interface Config {
  // 规范参数类型和返回值类型
  <T>(value: T): T;
}

let getDate: Config = function <T>(value: T): T {
  return value;
}

const data = getData<string>('123');
console.log(data);
  • 第二种写法
interface Config<T> {
  // 规范参数和返回值类型
  (value: T): T
}

// 接口方法
function getData<T>(value: T): T {
  return value;
}

// 使用接口
let myGetData: Config<string> = getData;
consoie.log(myGetData('123'));

tips 接口的泛型只针对函数类型的接口

类当做参数传入泛型类
class User {
  username: string | undefined;
  password: string | undefined;
  constructor(params: {
    usermame: string | undefined,
    password?: string | undefined
  }) {
    this.username = params.username;
    this.password = params.password;
  }
}

class Db<T> {
  add(user: T): boolean {
    console.log(user);
    return true;
  }
  updated(user: T, id: number): boolean {
    console.log(user, id);
    return true;
  }
}

let user = new User({
  username: 'Clearlove'
});

user.password = '123';

let db = new Db<User>();
db.add(user);
db.updated(user, 1);

tips 类的参数名和类型都做了约束

模块

内部模块成为命名空间,外部模块简称为模块,模块在起自身的作用域里执行,而不是在全局作用域。 定义在一个模块里的变量、函数、类等在模块外是不可见的,除非你明确的使用export形式导出它们。 对应的,如果想使用其他模块导出的变量、函数、类等,需要导入它们,可以使用import

// modules/db.ts
function getData(): any[] {
  console.log('获取数据');
  return [
    {
      userName: '张三'
    },
    {
      userName: '李四
    }
  ];
}

// 一个模块可以使用多次
export { getData };

// 一个模块只能使用一次
export default getData;
import { getData as getDbData } from './modules/db';
import getDbData from './modules/db';
getDbData();

tips 浏览器中不能直接使用,可在nodewebpack的环境中调试

命名空间

在代码量较大的情况下,为了避免各种变量命名冲突,可将相似功能的函数、类、接口等放置到命名空间内。TypeScript的命名空间可以将代码包裹起来,只对外部暴露需要在外部访问的对象。 命名空间和模块的区别:

  • 命名空间: 内部模块,主要用于组织代码,避免命名冲突
  • 模块: ts外部模块的简称,侧重代码的复用,一个模块里可能会有多个命名空间
// modules/Animal.ts
export namespace A {
  interface Animal {
    name: string;
    say(): void;
  }

  export class Dog implements Animal {
    name: string;
    constructor(name: string) {
      this.name = name;
    }
    say() {
      console.log(`我是${this.name}`);
    }
  }
}

export namespace B {
  interface Animal {
    name: string;
    eat(): void;
  }

  export class Dog implements Animal {
    name: string;
    constructor(name: string) {
      this.name = name;
    }
    say() {
      console.log(`Hello, my name is ${this.name}`);
    }
  }
}
import {A, B} from './modules/Animal';
const dog = new A.Dog('小马');
dog.say();

装饰器

装饰器本质上是一种特殊的函数,被应用在于:

  • 类属性
  • 类方法
  • 类访问器
  • 类方法的参数

所以应用装饰器其实很想是组合一系列函数,类似于高阶函数和类。 装饰器的语法十分简单,只需要在想使用的装饰器前面加上@符号,装饰器就会被应用到目标上:

function simpleDecorator() {
  console.log('i am a decorator!');
}

@simpleDecorator
class A {}

一共有5种装饰器可以被我们使用:

  1. 类装饰器
  2. 属性装饰器
  3. 方法装饰器
  4. 访问器装饰器
  5. 参数装饰器
@classDecorator
class Bird {
  // 属性装饰器
  @propertyDecorator
  name: string;

  // 方法装饰器
  @methodDecorator
  fly (
    // 参数装饰器
    @parameterDecorator
    meters: number
  ) {}
  // 访问器装饰器
  @accessorDecorator
  get egg() {}
}

执行

时机

装饰器只在解析执行时应用一次,例如:

function f() {
  console.log('apply decotator');
  return true;
}

@f
class A {}

// output: apply decorator

这里的代码会在终端中打印apply decorator,即使我们其实并没有使用类A

执行顺序

不同类型的装饰器执行顺序是明确定义的:

  1. 实例成员: 参数装饰器-> 方法/访问器/属性 装饰器
  2. 静态成员: 参数装饰器-> 方法/访问器/属性 装饰器
  3. 构造器:参数装饰器
  4. 类装饰器

例如:

function f(key: string) {
  console.log(`evaluate: ${key}`);
  return function() {
    console.log(`call: ${key}`);
  }
}

@f('class Decorator')
class A {
  @f('Static Property')
  static prop?: number;

  @f('Static Method')
  static method(@f('Static Methos Parameter') foo) {}

  constructor(@f('Constructor Parameter') foo) {}

  @f('Instance Method')
  method(@f('Instance Mthdos Parameter') foo) {}

  @f('Instance Propterty')
  prop?: number;
}

// evaluate Inastance Method
// evaluate Inastance Method Parameter
// call: Instace Method Parameter
// call: Instace Method
// evaluate Inastance Property
// call: Inastance Property
// evaluate Static Property
// call: Static Property
// evaluate Static Method
// evaluate Static Method Parameter
// call: Static Method Parameter
// call: Static Method
// evaluate: Class Decorator
// evaluate: Constructor Decorator
// call: Constructor Decorator
// call: Class Decorator

你也许会注意到,执行实例属性prop晚于实例方法method。然而执行静态属性static prop早于静态方法static method

这是因为对于属性/方法/访问器 装饰器而言,执行顺序取决于它们的声明顺序

然而,同一方法中不同参数的装饰器的执行顺序是相反的,最后一个参数的装饰器会被先执行:

function f(key: string) {
  console.log(`evaluate: ${key}`);
  return function () {
    console.log(`call: ${key}`);
  }
}

class A {
  method() {
    @f('Parameter Foo') foo,
    @f('Parameter Bar') bar
  } {}
}

// evaluate Parameter Foo
// evaluate Parameter Bar
// call Parameter Bar
// call Parameter Foo
多个装饰器的组合

可以对同一个目标应用多个装饰器,它们的组合顺序为:

  1. 求值外层装饰器
  2. 求值内层装饰器
  3. 调用内层装饰器
  4. 调用外层装饰器

例如:

function f(key: string) {
  console.log(`evaluate: ${key}`);
  return function () {
    console.log(`call: ${key}`);
  }
}

class A {
  @f('Outer Method')
  @f('Inner Method')
  method() {}
}

// evaluate: Outer Method
// evaluate: Inner Method
// call: Inner Method
// call: Outer Method

定义

类装饰器

应用于类构造器,其参数类的构造函数

function addAge(args: number) {
  return function(target: Function) {
    target.prototype.age = args;
  }
}

@addAge(18)
class Hello {
  name: string;
  age: number;
  constructor() {
    console.log('Hello World');
    this.name = 'Clearlove';
  }
}

console.log(Hello.prototype.age); // 18
const hello = new Hello();
console.log(hello.age); // 18
方法装饰器

它会被应用到方法的属性描述符上,可以用来监视、修改或者替换方法定义。 方法装饰器会在运行时传入下列三个参数:

  1. 对于静态成员来: 类的构造函数,对于实例成员:类的原型对象
  2. 成员的名字
  3. 成员的属性描述符(value: any, writable: boolean, enumerable: boolean, configurabel: boolean)
function addAge(constructor: Function) {
  constructor.prototype.age = 18;
}

function method(tagrget: any, propertyKey: string, descriptor: PropertyDescriptor) {
  console.log(target);
  console.log(`prop: ${propertyKey}`);
  console.log(`desc: ${JSON.stringify(descriptor)}` + '\n\n');
}

@addAge
class Hello {
  name: string;
  age: number;
  constructor() {
    console.log('Hello World');
    this.name = 'Clearlove';
  }

  @method
  hello(): string {
    return 'Instance method';
  }

  @method
  static sayHello(): string {
    return 'Static method';
  }
}

// Hello {hello: [Function]}
// prop: hello
// desc: {"writabel": true, "enumerabel": true, "configurable": true}

// { [Function: Hello] seyHello: [Function] }
// prop: sayHello
/// desc: {"writabel": true, "enumerabel": true, "configurable": true} 

假如修饰的是hello这个实例方法,第一个参数就是原型对象,也就是Hello.prototype。 加入修饰的是sayHello这个静态方法,第一个参数就是构造器constructor

访问器装饰器

访问器装饰器应用于访问器的属性描述符,可用于观察、修改或替换访问者的定义。 访问器不能再声明文件中使用,也不能在任何其他环境上下文中使用(例如在声明类中)。

访问器装饰器表达式会在运行时当作函数被调用,传人下列三个参数:

  1. 静态成员: 类的构造函数,实例成员: 类的原型对象
  2. 成员的名字
  3. 成员的属性描述符

例子:

function configurabele(value: boolean) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    descriptor.configurabel = value;
  }
}

class Point {
  private _x: number;
  private _y: number;

  constructor(x: number, y: number) {
    this._x = x;
    this._y = y;
  }

  @configurable(false)
  get _x() { return this_.x; }

  @configurable(false)
  get _y() { return this._y; }
}
属性装饰器

属性装饰器表达式会在运行时当作函数被调用,传人下列2个参数:

  1. 静态成员: 类的构造函数,实例成员: 类的原型对象
  2. 成员的名称
function log(target: any, propertyKey: string) {
  let value = target[properttKey];

  const getter = function () {
    console.log(`Getter for ${propertyKey} returned ${value}`);
    return value;
  }

  const setter = function (newVal) {
    console.log(`Set ${propertyKey} to ${newVa;}`);
    value = newVal;
  }

  if (delete this[propertyKey]) {
    Object.defineProperty(target, propertyKey, {
      get: getter,
      set: setter,
      enumerable: true,
      configurable: true
    });
  }
}

class Calculator {
  @log
  public num: number;
  square() {
    this.num * this.num;
  }
}

const cal = new Calculator();
cal.num = 2;
console.log(cal.square);

// Set num to 2
// Getter for num returned 2
// Getter for num returned 2
// 4
方法参数装饰器

参数装饰器表达式会在运行时当作函数被调用,传入下列3个参数:

  1. 静态成员: 类的构造函数,实例成员: 类的原型对象
  2. 参数的名称
  3. 参数再函数参数列表的索引
const parseConf = [];

// 在函数调用前执行格式化操作
function parseFunc(target: any, name, descriptor) {
  const originalMethod = descriptor.value;
  descriptor.value = function (...args: any[]) {
    for (let index = 0; index < parseConf.length; index++) {
      const type = parseConf[index];
      console.log(type);
      switch (type) {
        case 'number':
          args[index] = Number(args[index]);
          break;
        case 'string':
          args[index] = String(args[index]);
          break;
        case 'boolean':
          args[index] = String(args[index]) === 'true';
          break;
      }
      return originalMethod.apply(this, args);
    }
  };
  return descriptor;
}

// 向全局对象中添加对应的格式化信息
function parse(type) {
  return function (target, name, index) {
    parseConf[index] = type;
    console.log('parseConf[index]:', type);
  };
}

class Modal {
  @parseFunc
  public addOne(@parse('number') num) {
    console.log('num:', num);
    return num + 1;
  }
}

let modal = new Modal();
console.log(modal.addOne('10')); // 11
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 数字
  • 字符串
  • 数组
  • 布尔
  • 对象
  • undefined
  • 元组
  • 枚举
  • void
  • never
  • 任意类型
  • 函数
    • 函数声明
      • 方法参数
        • 剩余参数
          • 函数重载
          • 继承
          • 多态
          • 接口
            • 属性接口
              • 可索引接口
                • 类类型接口
                • 泛型
                  • 函数的泛型
                    • 类的泛型
                      • 接口的泛型
                        • 类当做参数传入泛型类
                        • 模块
                        • 命名空间
                        • 装饰器
                          • 执行
                            • 时机
                            • 执行顺序
                            • 多个装饰器的组合
                          • 定义
                            • 类装饰器
                            • 方法装饰器
                            • 访问器装饰器
                            • 属性装饰器
                            • 方法参数装饰器
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档