一個典型的TypeScript 題目

TypeScript 題

在讀深入理解typescript讀到infer這張遇到的這個題鸟辅,最后參考當(dāng)前文章

我們看完這些,我們發(fā)現(xiàn)typescript可以減少90%的一些拼寫、字段漏寫多律、傳參不匹配等基本錯誤崖疤。還可以增減代碼的健壯性移层,代碼的可維護性提升聂抢。

這個題目是一個很有代表意義的題蒂窒,在實際工作中也會遇到類似的問題。

問題定義

假設(shè)有一個叫EffectModule 的類

假設(shè)有一個叫 EffectModule 的類

這個對象上的方法只可能有兩種類型簽名:

interface Action<T> {
  payload?: T
  type: string
}

asyncMethod<T, U>(input: Promise<T>): Promise<Action<U>>

syncMethod<T, U>(action: Action<T>): Action<U>

這個對象上還可能有一些任意的非函數(shù)屬性:

interface Action<T> {
  payload?: T;
  type: string;
}

class EffectModule {
  count = 1;
  message = "hello!";

  delay(input: Promise<number>) {
    return input.then(i => ({
      payload: `hello ${i}!`,
      type: 'delay'
    }));
  }

  setMessage(action: Action<Date>) {
    return {
      payload: action.payload!.getMilliseconds(),
      type: "set-message"
    };
  }
}

現(xiàn)在有一個叫connect 的函數(shù)幔亥,它接受EffectModule 實例耻讽,將它變成另一個對象,這個對象上只有EffectModule的同名方法帕棉,但是方法的類型簽名被改變了:

asyncMethod<T, U>(input: Promise<T>): Promise<Action<U>>  變成了
asyncMethod<T, U>(input: T): Action<U> 
syncMethod<T, U>(action: Action<T>): Action<U>  變成了
syncMethod<T, U>(action: T): Action<U>

例子:
EffectModule 定義如下:

interface Action<T> {
  payload?: T;
  type: string;
}

class EffectModule {
  count = 1;
  message = "hello!";

  delay(input: Promise<number>) {
    return input.then(i => ({
      payload: `hello ${i}!`,
      type: 'delay'
    }));
  }

  setMessage(action: Action<Date>) {
    return {
      payload: action.payload!.getMilliseconds(),
      type: "set-message"
    };
  }
}

connect 之后:

type Connected = {
  delay(input: number): Action<string>
  setMessage(action: Date): Action<number>
}
const effectModule = new EffectModule()
const connected: Connected = connect(effectModule)

要求

題目鏈接 里面的 index.ts 文件中针肥,有一個 type Connect = (module: EffectModule) => any,將 any 替換成題目的解答香伴,讓編譯能夠順利通過慰枕,并且 index.tsconnected 的類型與:

type Connected = {
  delay(input: number): Action<string>;
  setMessage(action: Date): Action<number>;
}

完全匹配。

解答

  • 挑選函數(shù)
  • 取待推斷的變量類型
    補充
type FuncName<T> = { [P in keyof T]: T[P] extends Function ? P : never }[keyof T];

詳細

題目要求是把 type Connect 中的 any 替換掉,并符合:

type Connected = {
  delay(input: number): Action<string>;
  setMessage(action: Date): Action<number>;
}

并編譯通過即纲。
再把問題簡化一下捺僻,就是設(shè)計一個工具類型,讓題目中的EffectModule 的實例轉(zhuǎn)化為符合要求的Connected崇裁。

即:

type Connect = (module: EffectModule) => xxx ---> type Connected = {
  delay(input: number): Action<string>;
  setMessage(action: Date): Action<number>;
}

仔細觀察上面的偽代碼實例,Connected其實是一個對象類型,其中包含的 key-value 就是EffectModule 中的方法轉(zhuǎn)化而來的束昵,所以我們的入手處就是想辦法將EffectModule 中的方法轉(zhuǎn)化為對應(yīng)的Connected 中的 key-value拔稳。

type Connect = (module: EffectModule) => {
  ...
}

再觀察 Connected 的屬性與 EffectModule 的方法是不是有共同之處?他們的名字是一樣的,所以我們得先設(shè)計一個工具類型把 EffectModule 中的方法名取出來锹雏。
EffectModule,包含非方法的「屬性」巴比,所以得做個判斷,如果是屬性值類型是函數(shù)那么取出,否則不要取出

type FuncName<T> = { [P in keyof T]: T[P] extends Function ? P : never }[keyof T];

方法的類型簽名被改變了:

asyncMethod<T, U> = (input: Promise<T>) => Promise<Action<U>>  變成了
asyncMethodConnect<T, U> = (input: T) => Action<U> 

syncMethod<T, U> = (action: Action<T>) => Action<U>  變成了
syncMethodConnect<T, U> = (action: T) => Action<U>

著手轉(zhuǎn)化工作

type EffectModuleMethodsConnect<T> = T extends asyncMethod<infer U, infer V>
  ? asyncMethodConnect<U, V>
  : T extends syncMethod<infer U, infer V>
  ? syncMethodConnect<U, V>
  : never;

目前我們有兩個主要的工具類型EffectModuleMethodsConnect 負責(zé)類型的轉(zhuǎn)化,methodsPick負責(zé)取出方法名,現(xiàn)在我們先把方法名取出:

type EffectModuleMethods = FuncName<EffectModule>

最后

type Connect = (module: EffectModule) => {
  [M in EffectModuleMethods]: EffectModuleMethodsConnect<EffectModule[M]>
}
// import { expect } from "chai";

interface Action<T> {
  payload?: T;
  type: string;
}

class EffectModule {
  count = 1;
  message = "hello!";

  delay(input: Promise<number>) {
    return input.then(i => ({
      payload: `hello ${i}!`,
      type: 'delay'
    }));
  }

  setMessage(action: Action<Date>) {
    return {
      payload: action.payload!.getMilliseconds(),
      type: "set-message"
    };
  }
}

type MethodPick<T> = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T];

type asyncMethod<T, U> = (input: Promise<T>) => Promise<Action<U>>;
type asyncMethodConnect<T, U> = (input: T) => Action<U>;
type syncMethod<T, U> = (input: Action<T>) => Action<U>;
type syncMethodConnect<T, U> = (input: T) => Action<U>;
type EffectModuleMethodsConnect<T> = T extends asyncMethod<infer U, infer V>
  ? asyncMethodConnect<U, V>
  : T extends syncMethod<infer U, infer V>
  ? syncMethodConnect<U, V>
  : never;

  type EffectModuleMethods = MethodPick<EffectModule>;

// 修改 Connect 的類型轻绞,讓 connected 的類型變成預(yù)期的類型
type Connect = (module: EffectModule) => {
  [M in EffectModuleMethods]: EffectModuleMethodsConnect<EffectModule[M]>
};

const connect: Connect = m => ({
  delay: (input: number) => ({
    type: 'delay',
    payload: `hello 2`
  }),
  setMessage: (input: Date) => ({
    type: "set-message",
    payload: input.getMilliseconds()
  })
});

type Connected = {
  delay(input: number): Action<string>;
  setMessage(action: Date): Action<number>;
};

export const connected: Connected = connect(new EffectModule());

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末采记,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子政勃,更是在濱河造成了極大的恐慌唧龄,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奸远,死亡現(xiàn)場離奇詭異既棺,居然都是意外死亡,警方通過查閱死者的電腦和手機懒叛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門丸冕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人薛窥,你說我怎么就攤上這事胖烛。” “怎么了诅迷?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵佩番,是天一觀的道長。 經(jīng)常有香客問我竟贯,道長答捕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任屑那,我火速辦了婚禮拱镐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘持际。我一直安慰自己沃琅,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布蜘欲。 她就那樣靜靜地躺著益眉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪姥份。 梳的紋絲不亂的頭發(fā)上郭脂,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天,我揣著相機與錄音澈歉,去河邊找鬼展鸡。 笑死,一個胖子當(dāng)著我的面吹牛埃难,可吹牛的內(nèi)容都是我干的莹弊。 我是一名探鬼主播涤久,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼忍弛!你這毒婦竟也來了响迂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤细疚,失蹤者是張志新(化名)和其女友劉穎蔗彤,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惠昔,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡幕与,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了镇防。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片啦鸣。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖来氧,靈堂內(nèi)的尸體忽然破棺而出诫给,到底是詐尸還是另有隱情,我是刑警寧澤啦扬,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布中狂,位于F島的核電站,受9級特大地震影響扑毡,放射性物質(zhì)發(fā)生泄漏胃榕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一瞄摊、第九天 我趴在偏房一處隱蔽的房頂上張望勋又。 院中可真熱鬧,春花似錦换帜、人聲如沸楔壤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蹲嚣。三九已至,卻和暖如春祟牲,著一層夾襖步出監(jiān)牢的瞬間隙畜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工说贝, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留禾蚕,地道東北人。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓狂丝,卻偏偏與公主長得像换淆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子几颜,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,515評論 2 359