TypeScript基礎知識

基本數據類型

數字

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

經常使用於組合類型java

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

元組

可爲數組中的每一個參數定義相對應的類型node

const j: [number, string] = [1, '2'];

枚舉

enum err {
  first = 3,
  'second',
}

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

tipswebpack

  1. 若是未賦值的上一個值是數字,那麼這個未賦值的值就是上一個值 +1
  2. 若是未賦值的上一個值未賦值,那麼輸出的就是它的下標
  3. 若是未賦值的上一個值是非數字,那麼必須賦值

void

指定方法類型,表示沒有返回值,方法體中不能有returnweb

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

tipsapp

  1. public 在當前類、子類和類之外均可以訪問
  2. protected 在當前類、子類內部均可以訪問,類外部沒法訪問
  3. private 在當前類內部能夠訪問,子類和類外部沒法訪問
  4. 屬性不加修飾符,默認都是public

多態

經過抽象方法/方法重載,實現多態。多態的做用是用來定義標準
// 抽象父類ide

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
相關文章
相關標籤/搜索