typescript 難點梳理

1.new關鍵字在類型中的使用

泛型

在泛型里使用類類型

在TypeScript使用泛型創(chuàng)建工廠函數時万皿,需要引用構造函數的類類型。比如,

function create<T>(c: {new(): T; }): T {//這邊的new()不好理解
    return new c();
}

一個更高級的例子,使用原型屬性推斷并約束構造函數與類實例的關系门烂。

class BeeKeeper {
    hasMask: boolean;
}

class ZooKeeper {
    nametag: string;
}

class Animal {
    numLegs: number;
}

class Bee extends Animal {
    keeper: BeeKeeper;
}

class Lion extends Animal {
    keeper: ZooKeeper;
}

function createInstance<A extends Animal>(c: new () => A): A {
    return new c();
}

createInstance(Lion).keeper.nametag;  // typechecks!
createInstance(Bee).keeper.hasMask;   // typechecks!

查了不少資料,比較好的解釋是what is new() in Typescript?意思就是create函數的參數是構造函數沒有參數的T類的類型,同理兄淫,createInstance函數的參數是構造函數沒有參數的A類的類型诅福。
帶著疑問寫了測試代碼:


vscode依然報錯,仔細想下拖叙,createInstance函數return new c();這句話是類的實例化,所以傳進來的參數c是個類赂乐,而不是類的實例薯鳍,故要使用(c: new () => A)標明c是個類,而不是(c: Animal)類的實例挨措,從下面的調用也可以看出傳遞的是類而不是實例挖滤。
我們知道js里面是沒有類的,ES6里面的class也只是個語法糖浅役,編譯后依然為一個function斩松。所以去修飾一個class也就是修飾一個function,但是修飾的是構造函數觉既,所以這邊加以區(qū)別惧盹,前面有個new。


接口

類靜態(tài)部分與實例部分的區(qū)別

這邊同樣用到了關鍵字new()
第一個例子報錯了瞪讼,
官方解釋:
當你操作類和接口的時候钧椰,你要知道類是具有兩個類型的:靜態(tài)部分的類型和實例的類型。 你會注意到符欠,當你用構造器簽名去定義一個接口并試圖定義一個類去實現這個接口時會得到一個錯誤:


這里因為當一個類實現了一個接口時嫡霞,只對其實例部分進行類型檢查。 constructor存在于類的靜態(tài)部分希柿,所以不在檢查的范圍內诊沪。
因此养筒,我們應該直接操作類的靜態(tài)部分。 看下面的例子端姚,我們定義了兩個接口晕粪, ClockConstructor為構造函數所用和ClockInterface為實例方法所用。 為了方便我們定義一個構造函數 createClock寄锐,它用傳入的類型創(chuàng)建實例兵多。

interface ClockConstructor {
    new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
    tick:()=>void;
}

function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {//這個和泛型中使用類類型相同,
    return new ctor(hour, minute);//需要類型為ClockInterface的兩個參數的構造器類橄仆,只是二者寫法有點區(qū)別
}

class DigitalClock implements ClockInterface {//這邊實現的接口不能是直接的構造器
    constructor(h: number, m: number) { }
    tick() {
        console.log("beep beep");
    }
}
class AnalogClock implements ClockInterface {
    constructor(h: number, m: number) { }
    tick() {
        console.log("tick tock");
    }
}

let digital = createClock(DigitalClock, 12, 17);
let analog = createClock(AnalogClock, 7, 32);

因為createClock的第一個參數是ClockConstructor類型剩膘,在createClock(AnalogClock, 7, 32)里,會檢查AnalogClock是否符合構造函數簽名盆顾。

再結合react官方接口的書寫怠褐,

interface Component<P = {}, S = {}> extends ComponentLifecycle<P, S> { }
    class Component<P, S> {//這里全部是實例方法和屬性
        constructor(props?: P, context?: any);

        // Disabling unified-signatures to have separate overloads. It's easier to understand this way.
        // tslint:disable:unified-signatures
        setState<K extends keyof S>(f: (prevState: S, props: P) => Pick<S, K>, callback?: () => any): void;
        setState<K extends keyof S>(state: Pick<S, K>, callback?: () => any): void;
        // tslint:enable:unified-signatures

        forceUpdate(callBack?: () => any): void;
        render(): JSX.Element | null | false;

        // React.Props<T> is now deprecated, which means that the `children`
        // property is not available on `P` by default, even though you can
        // always pass children as variadic arguments to `createElement`.
        // In the future, if we can define its call signature conditionally
        // on the existence of `children` in `P`, then we should remove this.
        props: Readonly<{ children?: ReactNode }> & Readonly<P>;
        state: Readonly<S>;
        context: any;
        refs: {
            [key: string]: ReactInstance
        };
    }

 interface ComponentClass<P = {}> {
        new (props?: P, context?: any): Component<P, ComponentState>;//此處對Component做了修飾,規(guī)定react的構造函數類型
        propTypes?: ValidationMap<P>;//下面幾個全部是靜態(tài)方法和屬性
        contextTypes?: ValidationMap<any>;
        childContextTypes?: ValidationMap<any>;
        defaultProps?: Partial<P>;
        displayName?: string;
    }

為什么寫了interface Component又寫了class Component,interface Component沒有寫里面具體的實例方法和屬性,而寫了同名的class Component,是不是意味著class Component就是interface的具體實現浪汪,這里是index.d.ts文件盗蟆,應該在.ts文件里面不能這么寫

到此new()關鍵字在類型中的使用基本搞清楚了。

類裝飾器
function classDecorator<T extends {new(...args:any[]):{}}>(constructor:T) {
    return class extends constructor {
        newProperty = "new property";
        hello = "override";
    }
}

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

console.log(new Greeter("world"));

2. 裝飾器的使用

裝飾器(Decorator)在React中的應用
JavaScript 中的裝飾器是什么议谷?

3. 函數類型聲明的不同方式

1. 最常見的方式

函數聲明(Function Declaration)類型的,就是普通的具名函數

  function add(x: number, y: number): number {
    return x + y
  }
2. 函數表達式(Function Expression)類型聲明
  • 1.這種就是后面賦值號后面有個匿名或者具名函數,比較好理解的寫法,都在函數體內寫類型
  handle = (
    baseValue: number,
    increment: number,
  ): number => {
    return baseValue
  }
  • 2.給變量定義類型极祸,同時也在函數內部定義類型
  handle: (baseValue: number, increment: number) => number = (
    baseValue: number,
    increment: number,
  ): number => {
    return baseValue
  }
  • 3.將變量的類型抽取到接口中
interface IHandle {
  (baseValue: number, increment: number): number
}

 handle: IHandle = (baseValue: number, increment: number): number => {
    return baseValue
  }

既然前面的變量聲明了接口,那么后面的函數里面的類型就可以去掉了

interface IHandle {
  (baseValue: number, increment: number): number
}

 handle: IHandle = (baseValue,increment)=> {
    return baseValue
  }

但是發(fā)現個問題

interface IHandle {
  (baseValue: number, increment: number): number
}

 handle: IHandle = (baseValue)=> {
    return baseValue
  }

這么寫居然vscode沒有報錯怠晴,increment明明不是可選參數遥金,不是很理解,有待討論

查閱了typescript的官方文檔的函數章節(jié)

interface UIElement {
    addClickListener(onclick: (this: void, e: Event) => void): void;
}

這么寫的接口蒜田,和函數類型的接口聲明驚人的相似稿械,

interface IHandle {
  (baseValue: number, increment: number): number
}

差別在哪呢.函數類型的接口聲明是匿名的,而上面對象的類型聲明的函數是具名的冲粤,做了一下測試

var src:UIElement = function() {}
//報錯:
// Type '() => void' is not assignable to type 'UIElement'.
// Property 'addClickListener' is missing in type '() => void'.
// var src: UIElement

interface UIElement {
  addClickListener(name: string): void
}

var src: UIElement = {//不報錯
  addClickListener() {},
}

var src: UIElement = { //報錯
  addClickListener(name: string, age: number) {},
}
// Type '{ addClickListener(name: string, age: number): void; }' is not assignable to type 'UIElement'.
// Types of property 'addClickListener' are incompatible.
//   Type '(name: string, age: number) => void' is not assignable to type '(name: string) => void'.

看樣就是實際的函數的參數可以比接口少定義美莫,但是不能多定義
函數接口的是用來修飾變量的,當然包括函數的形參的修飾色解,以及返回值的修飾茂嗓,但是不能修飾具名函數

interface IHandle {
  props?: object
  (baseValue: number, increment: number): number
}

function source:IHandle(baseValue) {//這么修飾具名函數會報錯
  return baseValue
}

function source(baseValue): IHandle {//這么寫是可以的,表明返回了一個IHandle 的函數
  return baseValue
}

3. 怎樣修飾類(類類型)

class Greeter {
    static standardGreeting = "Hello, there";
    greeting: string;
    greet() {
        if (this.greeting) {
            return "Hello, " + this.greeting;
        }
        else {
            return Greeter.standardGreeting;
        }
    }
}

let greeter1: Greeter;
greeter1 = new Greeter();
console.log(greeter1.greet());

let greeterMaker: typeof Greeter = Greeter;
greeterMaker.standardGreeting = "Hey there!";

let greeter2: Greeter = new greeterMaker();
console.log(greeter2.greet());
  1. 我們一般都是修飾一個類的實例的科阎,怎么簡單的修飾類述吸,typeof是個很好的辦法,
    這個例子里,greeter1與之前看到的一樣蝌矛。 我們實例化 Greeter類道批,并使用這個對象。 與我們之前看到的一樣入撒。
    再之后隆豹,我們直接使用類。 我們創(chuàng)建了一個叫做 greeterMaker的變量茅逮。 這個變量保存了這個類或者說保存了類構造函數璃赡。 然后我們使用 typeof Greeter,意思是取Greeter類的類型献雅,而不是實例的類型碉考。 或者更確切的說,"告訴我 Greeter標識符的類型"挺身,也就是構造函數的類型侯谁。 這個類型包含了類的所有靜態(tài)成員和構造函數。 之后章钾,就和前面一樣墙贱,我們在 greeterMaker上使用new,創(chuàng)建Greeter的實例贱傀。

    也就是使用typeof ClassName
interface IPerson {
  age: number;
}
class Person {
  age: 99;
}

let p: typeof IPerson = Person;//這么寫會報錯的
  1. 使用構造函數接口修飾類
interface IPerson {
  age: number;
}

interface IPersonConstructor {
  new (): IPerson;
}

class Person {
  age: 99;
}

let p: IPersonConstructor = Person;

之前對new (): IPerson;這句話后面的返回值不是很理解惨撇,直到看到了將基類構造函數的返回值作為'this',也就是說new Person()的時候執(zhí)行的是構造函數府寒,那么構造函數就返回了Person的實例串纺,自然new (): IPerson;構造函數返回IPerson就很好理解了

4. keyof

比如有個interface a{
a1: 'a1';
a2: 'a2';
.....
.....
a100: 'a100';
}
然后又個類型要繼承這個interface的某一個value的值
比如 type anum = 'a1' | 'a2' | 'a3' | ....| 'a100',
應該怎么寫?

type anum = typeof a.a1 | typeof a.a2 | typeof a.a3 | ....| typeof a.a100;

有沒有簡單的寫法 那個interface 有可能隨時在變

a1到a100全要椰棘?

對全要

而且有可能 到a100以上 一直在添加

我想在添加的時候只添加interface type不用在修改了 有沒有辦法

key還是value

value

keyof
keyof.png

5. 函數名后面的感嘆號(非空斷言操作符)

    onOk = () => {
      this.props.onOk!(this.picker && this.picker.getValue());
      this.fireVisibleChange(false);
    }

react-component/m-picker
群里問了是非空操作符

再去搜索,在typescript 2.0的文檔里找到了榄笙,叫 非空斷言操作符

// 使用--strictNullChecks參數進行編譯
function validateEntity(e?: Entity) {
    // 如果e是null或者無效的實體邪狞,就會拋出異常
}

function processEntity(e?: Entity) {
    validateEntity(e);
    let s = e!.name;  // 斷言e是非空并訪問name屬性
}
vscode檢測

需要在tsconfig.json 里面加上"strictNullChecks": true,這樣vscode會自動檢測null和undefined

6. ts差集運算

Add support for literal type subtraction

具體應用在高階組件里面的props
TypeScript在React高階組件中的使用技巧

7. 方法重寫

重寫

方法重寫子類的參數需要跟父類一致,否則會報錯

8. 剩余參數

const renderWidget = ({ field, widget, ...restParams }) => {
  const min = restParams.min;
};

這段代碼的剩余參數restParams 怎么表示
感覺應該這么寫

const renderWidget = (
  {
    field,
    widget,
    ...restParams
  }: { field: string; widget: string;restParams: [key: string]: any },
  idx: number,
  primaryField: string
) => {
  const min = restParams.min;
};

但是還是報錯茅撞,其實應該省去restParams

const renderWidget = ({
  field,
  widget,
  ...restParams
}: {
  field: string;
  widget: string;
  [key: string]: any;
}) => {
  const min = restParams.min;
};

這樣就好了Destructuring a function parameter object and …rest

9. 元組推斷

群里看到的疑問帆卓,為什么最后推斷出是string|number

type TTuple = [string, number];

type Res = TTuple[number]; // string|number

一開始不理解,自己改下寫法

type TTuple = [string, number];

type Res = TTuple[string]; 

[圖片上傳失敗...(image-894ad4-1590418731585)]
再嘗試寫下去

type TTuple = [string, number];

type Res = TTuple[0];//string
type Res1 = TTuple[1];//number
type Res2 = TTuple[2];//報錯

[圖片上傳失敗...(image-e6ec46-1590418731585)]
TTuple[2]報錯

首先js中沒有元組概念米丘,數組中可以存放任何數據類型剑令,ts中把存放不同數據類型的數組叫做元組,這樣就很好理解TTuple[number]中的number就是索引index拄查,索引只能是數字吁津,而且不能越界,所以兩次報錯就好理解了,TTuple[number]為什么返回string|number是因為沒有指定具體的索引碍脏,只能推斷出兩種可能梭依,string或number

聯想到獲取接口的屬性的類型

interface IProps {
  name: string;
  age: number;
}

type IAge = IProps["name"];// string

const per: IAge = "geek";

10. type interface 泛型差異

下面實現類似于Record的代碼

interface Collection<T extends string, U> {
  [P in T]: U;
}

type Collection33<K extends string, T> = {
  [P in K]: T;
};
interface
interface

type完全沒問題,interface 的兩種寫法都會報錯典尾,好奇葩

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末役拴,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子钾埂,更是在濱河造成了極大的恐慌河闰,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件褥紫,死亡現場離奇詭異姜性,居然都是意外死亡,警方通過查閱死者的電腦和手機故源,發(fā)現死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門污抬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人绳军,你說我怎么就攤上這事印机。” “怎么了门驾?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵射赛,是天一觀的道長。 經常有香客問我奶是,道長楣责,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任聂沙,我火速辦了婚禮秆麸,結果婚禮上,老公的妹妹穿的比我還像新娘及汉。我一直安慰自己沮趣,他們只是感情好,可當我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布坷随。 她就那樣靜靜地躺著房铭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪温眉。 梳的紋絲不亂的頭發(fā)上缸匪,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天,我揣著相機與錄音类溢,去河邊找鬼凌蔬。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的龟梦。 我是一名探鬼主播隐锭,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼计贰!你這毒婦竟也來了钦睡?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤躁倒,失蹤者是張志新(化名)和其女友劉穎荞怒,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體秧秉,經...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡褐桌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了象迎。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片荧嵌。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖砾淌,靈堂內的尸體忽然破棺而出啦撮,到底是詐尸還是另有隱情,我是刑警寧澤汪厨,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布赃春,位于F島的核電站,受9級特大地震影響劫乱,放射性物質發(fā)生泄漏织中。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一衷戈、第九天 我趴在偏房一處隱蔽的房頂上張望狭吼。 院中可真熱鬧,春花似錦殖妇、人聲如沸搏嗡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至旧乞,卻和暖如春蔚润,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背尺栖。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工嫡纠, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓除盏,卻偏偏與公主長得像叉橱,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子者蠕,可洞房花燭夜當晚...
    茶點故事閱讀 44,779評論 2 354