typescript 學(xué)習(xí)總結(jié)

簡(jiǎn)介

typescript:javasscript的超集 ,添加了類型系統(tǒng)的 JavaScript,適用于任何規(guī)模的項(xiàng)目。
我們都知道JavaScript是一種弱類型的語(yǔ)言。而TypeScript增強(qiáng)了它的類型。
TypeScript 的[核心設(shè)計(jì)理念]:在完整保留 JavaScript 運(yùn)行時(shí)行為的基礎(chǔ)上,通過(guò)引入靜態(tài)類型系統(tǒng)來(lái)提高代碼的可維護(hù)性,減少可能出現(xiàn)的 bug。

TypeScript JavaScript
JavaScript 的超集用于解決大型項(xiàng)目的代碼復(fù)雜性 一種腳本語(yǔ)言,用于創(chuàng)建動(dòng)態(tài)網(wǎng)頁(yè)
可以在編譯期間發(fā)現(xiàn)并糾正錯(cuò)誤 是靜態(tài)類型 作為一種解釋型語(yǔ)言,只能在運(yùn)行時(shí)發(fā)現(xiàn)錯(cuò)誤,是動(dòng)態(tài)類型
強(qiáng)類型,支持靜態(tài)和動(dòng)態(tài)類型 弱類型,沒(méi)有靜態(tài)類型選項(xiàng)
最終被編譯成 JavaScript 代碼,使瀏覽器可以理解 可以直接在瀏覽器中使用
支持模塊、泛型和接口 不支持模塊,泛型或接口
社區(qū)的支持仍在增長(zhǎng),而且還不是很大 大量的社區(qū)支持以及大量文檔和解決問(wèn)題的支持

動(dòng)態(tài)類型是指在運(yùn)行時(shí)才會(huì)進(jìn)行類型檢查
靜態(tài)類型是指編譯階段就能確定每個(gè)變量的類型

1. 安裝typescript

npm install -g typescript

編譯ts文件(生成對(duì)應(yīng)的js文件,)

tsc  xxx.ts

[官方提供的在線編寫typescript網(wǎng)址]

2. 數(shù)據(jù)類型

原始數(shù)據(jù)類型:Boolean , String , Number , null , undefined ,Symbol
其他數(shù)據(jù)類型: Arrsy , Enum , Any , Never , Object , Void , Unknow , Tuple

  • Symbol
    es6中新增的一個(gè)類型,表示獨(dú)一無(wú)二的值.Symbol 值通過(guò)Symbol()函數(shù)生成。這就是說(shuō),對(duì)象的屬性名現(xiàn)在可以有兩種類型,一種是原來(lái)就有的字符串,另一種就是新增的 Symbol 類型。凡是屬性名屬于 Symbol 類型,就都是獨(dú)一無(wú)二的,可以保證不會(huì)與其他屬性名產(chǎn)生沖突。
let a  = Symbol()
let b = Symbol('b')
let d = Symbol()
a == b // false
  • unknow ,any
    在ts中any被成為全局超級(jí)類型,可以逃避類型檢測(cè)
    unknow也是另一種超級(jí)類型,但是unknow類型只能被賦值 unknow類型 和any類型
let value:unknown ;
let value2: any =value 
let value3: boolean = value // Type 'unknown' is not assignable to type 'boolean
  • tuple
    一般數(shù)組是由同種類型的值組成,但是元組可以滿足數(shù)組中含有不同類型的要求,
let data:[boolean , number , string] = [true ,123,'111']

3. 類型推論

如果沒(méi)有明確的指定類型,那么 TypeScript 會(huì)依照類型推論(Type Inference)的規(guī)則推斷出一個(gè)類型。

let c = 12345;
console.log(typeof c) // number
c = 'hahahha' // Error: Type 'string' is not assignable to type 'number'

4. 聯(lián)合類型

取值可以為多種類型中的一種

let a :number | string | boolean;
a = 4
a = 'www'
a = true

當(dāng) TypeScript 不確定一個(gè)聯(lián)合類型的變量到底是哪個(gè)類型的時(shí)候,我們只能訪問(wèn)此聯(lián)合類型的所有類型里共有的屬性或方法:

5. class

雖然 JavaScript 中有類的概念,但是可能大多數(shù) JavaScript 程序員并不是非常熟悉類,這里對(duì)類相關(guān)的概念做一個(gè)簡(jiǎn)單的介紹。

  • 類(Class):定義了一件事物的抽象特點(diǎn),包含它的屬性和方法
  • 對(duì)象(Object):類的實(shí)例,通過(guò) new 生成
  • 面向?qū)ο螅∣OP)的三大特性:封裝、繼承、多態(tài)
  • 封裝(Encapsulation):將對(duì)數(shù)據(jù)的操作細(xì)節(jié)隱藏起來(lái),只暴露對(duì)外的接口。外界調(diào)用端不需要(也不可能)知道細(xì)節(jié),就能通過(guò)對(duì)外提供的接口來(lái)訪問(wèn)該對(duì)象,同時(shí)也保證了外界無(wú)法任意更改對(duì)象內(nèi)部的數(shù)據(jù)
  • 繼承(Inheritance):子類繼承父類,子類除了擁有父類的所有特性外,還有一些更具體的特性
  • 多態(tài)(Polymorphism):由繼承而產(chǎn)生了相關(guān)的不同的類,對(duì)同一個(gè)方法可以有不同的響應(yīng)。比如 CatDog 都繼承自 Animal,但是分別實(shí)現(xiàn)了自己的 eat 方法。此時(shí)針對(duì)某一個(gè)實(shí)例,我們無(wú)需了解它是 Cat 還是 Dog,就可以直接調(diào)用 eat 方法,程序會(huì)自動(dòng)判斷出來(lái)應(yīng)該如何執(zhí)行 eat
  • 存取器(getter & setter):用以改變屬性的讀取和賦值行為
  • 修飾符(Modifiers):修飾符是一些關(guān)鍵字,用于限定成員或類型的性質(zhì)。比如 public 表示公有屬性或方法
  • 抽象類(Abstract Class):抽象類是供其他類繼承的基類,抽象類不允許被實(shí)例化。抽象類中的抽象方法必須在子類中被實(shí)現(xiàn)
  • 接口(Interfaces):不同類之間公有的屬性或方法,可以抽象成一個(gè)接口。接口可以被類實(shí)現(xiàn)(implements)。一個(gè)類只能繼承自另一個(gè)類,但是可以實(shí)現(xiàn)多個(gè)接口
class Greeter {
  // 靜態(tài)屬性
  static cname: string = "Greeter";
  // 成員屬性
  greeting: string;

  // 構(gòu)造函數(shù) - 執(zhí)行初始化操作
  constructor(message: string) {
    this.greeting = message;
  }

  // 靜態(tài)方法
  static getClassName() {
    return "Class name is Greeter";
  }

  // 成員方法
  greet() {
    return "Hello, " + this.greeting;
  }
}

let greeter = new Greeter("world");

那么成員屬性與靜態(tài)屬性,成員方法與靜態(tài)方法有什么區(qū)別呢?可以直接看一下編譯生成的 ES5 代碼:

var Greeter = (function () {
    // 構(gòu)造函數(shù) - 執(zhí)行初始化操作
    function Greeter(message) {
        this.greeting = message;
    }
    // 靜態(tài)方法
    Greeter.getClassName = function () {
        return "Class name is Greeter";
    };
    // 成員方法
    Greeter.prototype.greet = function () {
        return "Hello, " + this.greeting;
    };
    // 靜態(tài)屬性
    Greeter.cname = "Greeter";
    return Greeter;
}());
var greeter = new Greeter("world");
greeter.getClassName() // Property 'getClassName' does not exist on type 'Greeter'. Did you mean to access the static member 'Greeter.getClassName' instead?
Greeter.getClassName() // Class name is Greeter

私有字段

class Person {
  #name: string;

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

  greet() {
    console.log(`Hello, my name is ${this.#name}!`);
  }
}

let semlinker = new Person("Semlinker");

semlinker.#name;
//     ~~~~~
// Property '#name' is not accessible outside class 'Person'
// because it has a private identifier.

與常規(guī)屬性(甚至使用 private 修飾符聲明的屬性)不同,私有字段要牢記以下規(guī)則:
私有字段以 # 字符開(kāi)頭,有時(shí)我們稱之為私有名稱;
每個(gè)私有字段名稱都唯一地限定于其包含的類;
不能在私有字段上使用 TypeScript 可訪問(wèn)性修飾符(如 public 或 private);
私有字段不能在包含的類之外訪問(wèn),甚至不能被檢測(cè)到。

抽象類
使用 abstract 關(guān)鍵字聲明的類,我們稱之為抽象類。抽象類不能被實(shí)例化,因?yàn)樗锩姘粋€(gè)或多個(gè)抽象方法。所謂的抽象方法,是指不包含具體實(shí)現(xiàn)的方法:

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

  abstract say(words: string) :void;
}

// Cannot create an instance of an abstract class.(2511)
const lolo = new Person(); // Error

抽象類不能被直接實(shí)例化,我們只能實(shí)例化實(shí)現(xiàn)了所有抽象方法的子類。

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

  // 抽象方法
  abstract say(words: string) :void;
}

class Developer extends Person {
  constructor(name: string) {
    super(name);
  }
  
  say(words: string): void {
    console.log(`${this.name} says ${words}`);
  }
}

const lolo = new Developer("lolo");
lolo.say("I love ts!"); // lolo says I love ts!

6. implements / extends

  • implements
    實(shí)現(xiàn), 一個(gè)新的類,從父類或者接口實(shí)現(xiàn)所有的屬性和方法,同時(shí)可以重寫屬性和方法,包含一些新的功能
  • extends
    繼承,一個(gè)新的接口或者類,從父類或者接口繼承所有的屬性和方法,不可以重寫屬性,但可以重寫方法
interface Test {
    x:number;
    y:number
}

class TestDemo implements Test{
    x = 1; 
    y = 2;
    sayHello(){
        console.log(`${this.x}--${this.y}say hello`)
    }
}

class Test2Demo extends TestDemo{
    y = 4 
    sayHello(){
        console.log(`${this.x}--${this.y}say hello 2`) 
    }
} 

const a = new Test2Demo()
const b = new TestDemo()
a.sayHello();
b.sayHello()
//1--4say hello 2
//1--2say hello
  • 只有類才能實(shí)現(xiàn)和繼承類
  • 可以多實(shí)現(xiàn)和多繼承

7. 泛型

泛型(Generics)是指在定義函數(shù)、接口或類的時(shí)候,不預(yù)先指定具體的類型,而在使用的時(shí)候再指定類型的一種特性。

function createArray(length: number, value: any): Array<any> {
    let result = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}

createArray(3, 'x'); // ['x', 'x', 'x']

無(wú)法精準(zhǔn)定位返回的數(shù)據(jù)類型,any可以是任意類型

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'); // ['x', 'x', 'x']

這樣可以精準(zhǔn)推算出返回的數(shù)據(jù)類型

其中 T 代表 Type,在定義泛型時(shí)通常用作第一個(gè)類型變量名稱。但實(shí)際上 T 可以用任何有效名稱代替。除了 T 之外,以下是常見(jiàn)泛型變量代表的意思:

K(Key):表示對(duì)象中的鍵類型;
V(Value):表示對(duì)象中的值類型;
E(Element):表示元素類型。

  • 泛型接口
    當(dāng)然也可以使用含有泛型的接口來(lái)定義函數(shù)的形狀:
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] = value;
    }
    return result;
}

createArray(3, 'x'); // ['x', 'x', 'x']
  • 泛型類
class GenericNumber<T> {
  zeroValue: T;
  add: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function (x, y) {
  return x + y;
};
  • 泛型約束
    在函數(shù)內(nèi)部使用泛型變量的時(shí)候,由于事先不知道它是哪種類型,所以不能隨意的操作它的屬性或方法:所有可以對(duì)泛型進(jìn)行約束,必須含有特定的屬性
function loggingIdentity<T extends TypeConstraint>(arg: T): T {
    console.log(arg.length);
    console.log(arg.personName);
    return arg
}

loggingIdentity({personName:'hahah' ,length:4}) // 4 , 'hahah'
  • 在泛型約束中使用類型參數(shù)
    可以聲明受另一個(gè)類型參數(shù)約束的類型參數(shù)。
function getProperty<T, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}
 
let x = { a: 1, b: 2, c: 3, d: 4 };
 
getProperty(x, "a"); 1
getProperty(x, "m"); //Error Argument of type '"m"' is not assignable to parameter of type '"a" | "b" | "c" | "d"'.

8. 裝飾器

[參考]

8.1 裝飾器是什么
  • 它是一個(gè)表達(dá)式
  • 該表達(dá)式被執(zhí)行后,返回一個(gè)函數(shù)
  • 函數(shù)的入?yún)⒎謩e為 target、name 和 descriptor
  • 執(zhí)行該函數(shù)后,可能返回 descriptor 對(duì)象,用于配置 target 對(duì)象
8.2 裝飾器的分類
  • 類裝飾器(Class decorators)
  • 屬性裝飾器(Property decorators)
  • 方法裝飾器(Method decorators)
  • 參數(shù)裝飾器(Parameter decorators)

需要注意的是,若要啟用實(shí)驗(yàn)性的裝飾器特性,你必須在命令行或 tsconfig.json 里啟用 experimentalDecorators 編譯器選項(xiàng):

Class decorators

// Class decorators
function classDecorator<T extends {new(...args:any[]):{}}>(constructor: T){
    return class extends constructor{
        newProperty ='new property';
        hello = 'override';
    }
}

function classFactoryDecorator(num:number){
   return function(constructor:Function){
       constructor.prototype.luckyNumber = Math.floor(Math.random() * num) 
   }
}

@classDecorator
@classFactoryDecorator(20)
class TestClass {
    property = "property";
    hello: string;
    constructor(m: string) {
        this.hello = m;
    }
}

let greeting = new TestClass('hah');

console.log(greeting.hello , greeting.property , greeting.luckyNumber)
// "override",  "property",  9 

Property decorators

// Property decorators

function Min(num:number){
   return function(target:Object , properrtyKey:string){
       let value:string;
       const setter = function(newVal:string){
           if(newVal.length < num){
            throw new Error(`Your password should be bigger than ${num}`)
           }
          value = newVal
       }

       const getter = function(){
           return value
       }
      Object.defineProperty(target , properrtyKey , {
         get:getter,
         set:setter
      })
   }
}


class Password{
    @Min(4)
    password:string;

    constructor(password:string){
      this.password = password
    }
}


let password = new Password('121212')
console.log(password.password)
let password2 = new Password('12') // Error :Your password should be bigger than 4 

Method decorators

// declare type MethodDecorator = <T>(target:Object, propertyKey: string | symbol,      
//     descriptor: TypePropertyDescript<T>) => TypedPropertyDescriptor<T> | void;


function logger(target:Object , propertyKey:string , descriptor :PropertyDescriptor){
   console.log(`${propertyKey}is working`)
}


function doubleResult(){
    return function(target:Object , propertyKey:string , descriptor :PropertyDescriptor){
        return {
            value: function(...args:any[]){
                var result = descriptor.value.apply(this, args) * 2;
                return result;
            }
        }
     }
}


class MethodDecoratorClass {
    newName:string ;
    constructor(newName:string){
        this.newName = newName;
    }

    @logger
    changeName(name:string){
        this.newName = name
    }

    @doubleResult()
    sum(x:number , y:number){
        return x + y
    }
}

let demo = new MethodDecoratorClass('hello');
demo.changeName('hahahh')
console.log(demo.newName)

console.log(demo.sum(1 ,2))

// [LOG]: "changeNameis working" 
// [LOG]: "hahahh" 
// [LOG]: 6 

Parameter decorators

function notNull(target: any, propertyKey: string, parameterIndex: number) {
    console.log("param decorator notNull function invoked ");
    Validator.registerNotNull(target, propertyKey, parameterIndex);
}

function validate(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log("method decorator validate function invoked ");
    let originalMethod = descriptor.value;
    //wrapping the original method
    descriptor.value = function (...args: any[]) {//wrapper function
        if (!Validator.performValidation(target, propertyKey, args)) {
            console.log("validation failed, method call aborted: " + propertyKey);
            return;
        }
        let result = originalMethod.apply(this, args);
        return result;
    }
}
class Validator {
    private static notNullValidatorMap: Map<any, Map<string, number[]>> = new Map();

    //todo add more validator maps
    static registerNotNull(target: any, methodName: string, paramIndex: number): void {
        let paramMap: Map<string, number[]> = this.notNullValidatorMap.get(target);
        if (!paramMap) {
            paramMap = new Map();
            this.notNullValidatorMap.set(target, paramMap);
        }
        let paramIndexes: number[] = paramMap.get(methodName);
        if (!paramIndexes) {
            paramIndexes = [];
            paramMap.set(methodName, paramIndexes);
        }
        paramIndexes.push(paramIndex);
    }
        static performValidation(target: any, methodName: string, paramValues: any[]): boolean {
        let notNullMethodMap: Map<string, number[]> = this.notNullValidatorMap.get(target);
        if (!notNullMethodMap) {
            return true;
        }
        let paramIndexes: number[] = notNullMethodMap.get(methodName);
        if (!paramIndexes) {
            return true;
        }
        let hasErrors: boolean = false;
        for (const [index, paramValue] of paramValues.entries()) {
            if (paramIndexes.indexOf(index) != -1) {
                if (!paramValue) {
                    console.error("method param at index " + index + " cannot be null");
                    hasErrors = true;
                }
            }
        }
        return !hasErrors;
    }
}

class Task {
    @validate
    run(@notNull name: string): void {
        console.log("running task, name: " + name);
    }
}

console.log("-- creating instance --");
let task: Task = new Task();
console.log("-- calling Task#run(null) --");
//task.run(null);
console.log("----------------");
console.log("-- calling Task#run('test') --");
task.run("test");

// [LOG]: "param decorator notNull function invoked " 
// [LOG]: "method decorator validate function invoked " 
// [LOG]: "-- creating instance --" 
// [LOG]: "-- calling Task#run(null) --" 
// [LOG]: "----------------" 
// [LOG]: "-- calling Task#run('test') --" 
// [LOG]: "running task, name: test" 
9.namespace

隨著方法屬性的增多,以便于在記錄它們類型的同時(shí)還不用擔(dān)心與其它對(duì)象產(chǎn)生命名沖突。 因此,我們把屬性和方法包裹到一個(gè)命名空間內(nèi),而不是把它們放在全局命名空間下。

namespace Test {
   export interface TestInterface{
      location?:string
   }
   export const personName:string = 'hahahah';
   
   export  const getPersonName = () => {
      return personName
   }
}

let test:Test.TestInterface;
let nameNew = Test.getPersonName();
console.log(nameNew) // 'hahahah'

9.JSX

JSX是一種嵌入式的類似XML的語(yǔ)法。 它可以被轉(zhuǎn)換成合法的JavaScript,盡管轉(zhuǎn)換的語(yǔ)義是依據(jù)不同的實(shí)現(xiàn)而定的。 JSX因React框架而流行,但也存在其它的實(shí)現(xiàn)。 TypeScript支持內(nèi)嵌,類型檢查以及將JSX直接編譯為JavaScript。

想要使用JSX必須做兩件事:

  1. 給文件一個(gè).tsx擴(kuò)展名
  2. 啟用jsx選項(xiàng)

TypeScript具有三種JSX模式:preserve,reactreact-native。 這些模式只在代碼生成階段起作用 - 類型檢查并不受影響。 在preserve模式下生成代碼中會(huì)保留JSX以供后續(xù)的轉(zhuǎn)換操作使用(比如:Babel)。 另外,輸出文件會(huì)帶有.jsx擴(kuò)展名。 react模式會(huì)生成React.createElement,在使用前不需要再進(jìn)行轉(zhuǎn)換操作了,輸出文件的擴(kuò)展名為.js。 react-native相當(dāng)于preserve,它也保留了所有的JSX,但是輸出文件的擴(kuò)展名是.js

10. interface和type的區(qū)別

  1. typeb不能聲明合并
interface Window {
  title: string;
}

interface Window {
  ts: TypeScriptAPI;
}

// 合并后的 Window 接口
// {
//   title: string;
//   ts: TypeScriptAPI;
// }
  1. type可以用來(lái)創(chuàng)建聯(lián)合類型,交叉類型等復(fù)雜類型
  2. type支持類型映射
  3. type可以用于定義元組和基本類型
    總結(jié): interface 用于定義對(duì)象的結(jié)構(gòu),因?yàn)榭梢詫?shí)現(xiàn)類的繼承和實(shí)現(xiàn),支持聲明合并,type適合定義更復(fù)雜和靈活的類型。
2. 提取公共屬性
  1. keyof , Extract提取共有的鍵
  2. 利用提取的共有鍵,構(gòu)建新類型
  type CommKey<T, U> = Extract<keyof T , keyof U>
  type CommonProperties<T , U> = Pick<T ,CommonKeys<T,U>>
  
  type A = {
  name: string;
  age: number;
  location: string;
  };

type B = {
  name: string;
  age: number;
  gender: string;
};

// 提取共有鍵
type CommonKeys<T, U> = Extract<keyof T, keyof U>;

// 基于共有鍵生成新類型
type CommonProperties<T, U> = Pick<T, CommonKeys<T, U>>;

// 應(yīng)用到具體類型 A 和 B 上
type ABCommonProperties = CommonProperties<A, B>;

// 測(cè)試
const example: ABCommonProperties = {
  name: "Alice",
  age: 30,
  // location: "NY", // Error: 'location' is not a common property
  // gender: "female" // Error: 'gender' is not a common property
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容