TypeScript 類型挑戰(zhàn) Easy

學(xué)習(xí) TypeScript 類型

  • 項(xiàng)目地址 Github
  • 項(xiàng)目描述: 高質(zhì)量的類型可以幫助提高項(xiàng)目的可維護(hù)性,同時(shí)避免潛在的錯(cuò)誤张咳。

優(yōu)勢(shì)

  • 提高 TypeScript 技術(shù)能更好的在編譯階段處理一些問(wèn)題, 這些問(wèn)題可能在運(yùn)行時(shí)侵占更多的資源. 例如一個(gè)只讀的對(duì)象屬性, 在 JavaScript 中的一種實(shí)現(xiàn)方式就是在該屬性的 setter 中拋出異常. 在TS中使用關(guān)鍵字即可在編譯階段就阻止這樣的代碼寫(xiě)入程序.
// 運(yùn)行時(shí)
class Husky {
  set a () {
    throw new Error('a is a readonly propertype')
  }
}

class Husky {
  readonly a 
}
  • 提升程序穩(wěn)定性, 可預(yù)期的代碼行為, 更好的代碼說(shuō)明性.

劣勢(shì)

  • 引入TypeScript 就是引入復(fù)雜度, 項(xiàng)目必須編寫(xiě)更多運(yùn)行時(shí)以外的說(shuō)明代碼.
  • 對(duì)程序人員技術(shù)要求更高, 一個(gè)復(fù)雜的類型約束可能非常不好讀. 項(xiàng)目最后很有可能編程 AnyScript
  • 并完全不能替代運(yùn)行時(shí)的類型校驗(yàn), 因?yàn)閿?shù)據(jù)很有可能都是后端傳遞過(guò)來(lái)的無(wú)法控制. Superstruct 運(yùn)行時(shí)類型檢查

Easy 版需要注意的事情

  1. 需要了解基本的語(yǔ)法含義比如 extends keyof typeof infer ... 他們有些和 JavaScript 語(yǔ)法形式一致. 但含義并不相同. 類型操作是在類型命名空間. 值操作(即JavaScript)是在值命名空間進(jìn)行. 所以在編譯后抹去所有的類型操作, 并不會(huì)影響程序運(yùn)行.
  2. 基于上一條, 剛進(jìn)行類型挑戰(zhàn)的人. 比如剛才的我, 很難注意到類型和值邊界. 所有的類型的操作所返回的東西依然是類型, 盡管它看起來(lái)是一個(gè)值. 比如: type Exclude<'a' | 'b', 'a'> => 'b'

你可以去源碼倉(cāng)庫(kù)去在線實(shí)操一下. 更容易掌握. 更多的類型技術(shù)需要更長(zhǎng)的時(shí)間進(jìn)行積累. 下面我們開(kāi)始吧.

Note:

  • 有意思的是校驗(yàn)挑戰(zhàn)的正確性的程序本身也是 TypeScript 的類型語(yǔ)法. 所以 TypeScript 自稱是圖靈完備的. 但是項(xiàng)目中的這句話被橫線劃掉了, 這看起來(lái)就很有意思了, 我們隨著挑戰(zhàn)的進(jìn)行繼續(xù)看下去吧.
  • 練習(xí)時(shí)也可以使用編輯器新建TS文件, 通過(guò)這種方式進(jìn)行校驗(yàn).

Pick

Easy, #union, #built-in

實(shí)現(xiàn) TS 內(nèi)置的 Pick<T, K>帝洪,但不可以使用它。

從類型 T 中選擇出屬性 K脚猾,構(gòu)造成一個(gè)新的類型葱峡。

interface Todo {
  title: string
  description: string
  completed: boolean
}

type TodoPreview = MyPick<Todo, 'title' | 'completed'>

const todo: TodoPreview = {
    title: 'Clean room',
    completed: false,
}

答案

type MyPick<T, U extends keyof T> = {
  [K in U]: T[K]
}

// U = keyof T = ['title', 'description', 'completed']
// K = 循環(huán) U

Readonly

Easy, #union, #built-in

不要使用內(nèi)置的 Readonly<T>,自己實(shí)現(xiàn)一個(gè)婚陪。

該 Readonly 會(huì)接收一個(gè) 泛型參數(shù)族沃,并返回一個(gè)完全一樣的類型,只是所有屬性都會(huì)被 readonly 所修飾。

也就是不可以再對(duì)該對(duì)象的屬性賦值脆淹。

interface Todo {
  title: string
  description: string
}

const todo: MyReadonly<Todo> = {
  title: "Hey",
  description: "foobar"
}

todo.title = "Hello" // Error: cannot reassign a readonly property
todo.description = "barFoo" // Error: cannot reassign a readonly property

答案

type MyReadonly<T> = {
  readonly[K in keyof T]: T[K]
}

Tuple to Object

Easy

傳入一個(gè)元組類型常空,將這個(gè)元組類型轉(zhuǎn)換為對(duì)象類型,這個(gè)對(duì)象類型的鍵/值都是從元組中遍歷出來(lái)盖溺。

const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const

type result = TupleToObject<typeof tuple> 
// expected { tesla: 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}

答案

type TupleToObject<T extends readonly any[]> = {
  [K in T[number]]: K
}

// T = ['string', 'string', 'string', 'string']
// T[number] = tuple
// K = 循環(huán) T[number] = 循環(huán) tuple
// readonly 對(duì)應(yīng) as const

First of Array

Easy, #array

實(shí)現(xiàn)一個(gè)通用 First<T>漓糙,它接受一個(gè)數(shù)組T并返回它的第一個(gè)元素的類型。

type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]

type head1 = First<arr1> // expected to be 'a'
type head2 = First<arr2> // expected to be 3

答案

type First<T extends any[]> = T extends [] ? never : T[0]

Length of Tuple

創(chuàng)建一個(gè)通用的 Length烘嘱,接受一個(gè) readonly 的數(shù)組昆禽,返回這個(gè)數(shù)組的長(zhǎng)度。

type tesla = ['tesla', 'model 3', 'model X', 'model Y']
type spaceX = ['FALCON 9', 'FALCON HEAVY', 'DRAGON', 'STARSHIP', 'HUMAN SPACEFLIGHT']

type teslaLength = Length<tesla> // expected 4
type spaceXLength = Length<spaceX> // expected 5

答案

type Length<T extends readonly any[]> = T['length']

Exclude

Easy, #built-in

實(shí)現(xiàn)內(nèi)置的 Exclude <T, U> 類型蝇庭,但不能直接使用它本身醉鳖。

type MyExclude<'a' | 'b' | 'c', 'a'>  // Exclude<'a' | 'b' | 'c', 'a'>, 'b' | 'c'
type MyExclude<string | number | (() => void), Function> // Exclude<string | number | (() => void), Function> // string | number

答案

type MyExclude<T, U> = T extends U ? never : T

// Exclude = T - U

Awaited

Easy, #promise, #built-in

假如我們有一個(gè) Promise 對(duì)象,這個(gè) Promise 對(duì)象會(huì)返回一個(gè)類型哮内。在 TS 中盗棵,我們用 Promise 中的 T 來(lái)描述這個(gè) Promise 返回的類型。請(qǐng)你實(shí)現(xiàn)一個(gè)類型北发,可以獲取這個(gè)類型纹因。

type X = MyAwaited<Promise<string>> // string
type Y = MyAwaited<Promise<{ field: number }>> // { field: number }
type Z = MyAwaited<Promise<Promise<Promise<string | number>>>> // string | number

答案

type MyAwaited<T extends Promise<any>> = T extends Promise<infer U>
  ? U extends Promise<any>
    ? MyAwaited<U>
    : U
  : T

If

Easy, #utils

實(shí)現(xiàn)一個(gè) IF 類型,它接收一個(gè)條件類型 C 琳拨,一個(gè)判斷為真時(shí)的返回類型 T 瞭恰,以及一個(gè)判斷為假時(shí)的返回類型 F袱巨。 C 只能是 true 或者 false济赎, TF 可以是任意類型。

type A = If<true, 'a', 'b'>  // expected to be 'a'
type B = If<false, 'a', 'b'> // expected to be 'b'

答案

type If<C extends boolean, T, F> = C extends true ? T : F

Concat

Easy, #array

在類型系統(tǒng)里實(shí)現(xiàn) JavaScript 內(nèi)置的 Array.concat 方法栓票,這個(gè)類型接受兩個(gè)參數(shù)密任,返回的新數(shù)組類型應(yīng)該按照輸入?yún)?shù)從左到右的順序合并為一個(gè)新的數(shù)組陕截。

type Result = Concat<[1], [2]> // expected to be [1, 2]

答案

type Concat<T extends any[], U extends any[]> = [...T, ...U]

Includes

Easy, #array

在類型系統(tǒng)里實(shí)現(xiàn) JavaScript 的 Array.includes 方法,這個(gè)類型接受兩個(gè)參數(shù)批什,返回的類型要么是 true 要么是 false。

type isPillarMen = Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'> // expected to be `false`

答案

type Equal<X, Y> = <T>() => T extends X ? 1 : 2 extends <T>() => T extends Y ? 1 : 2 ? true : false

type Includes<T extends readonly any[], U> = 
  T extends [infer First, ...infer Rest]
    ? Equal<U, First> extends true
      ? true
      : Includes<Rest, U>
    : false

Push

Easy, #array

在類型系統(tǒng)里實(shí)現(xiàn)通用的 Array.push

type Result = Push<[1, 2], '3'> // [1, 2, '3']
type Push<T extends any[], U> = [...T, U]

Unshift

Easy, #array

在類型系統(tǒng)里實(shí)現(xiàn)通用的 Array.unshift

type Result = Unshift<[1, 2], 0> // [0, 1, 2,]
type Unshift<T extends any[], U> = [U, ...T]

Parameters

Easy, #infer, #tuple, #built-in

實(shí)現(xiàn)內(nèi)置的 Parameters 類型

const foo = (arg1: string, arg2: number): void => {}
const bar = (arg1: boolean, arg2: { a: 'A' }): void => {}
const baz = (): void => {}

type MyParameters<typeof foo> // [string, number]
type MyParameters<typeof bar> // [boolean, { a: 'A' }]
type MyParameters<typeof baz> // []
]
type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer U) => any ? U : never
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末社搅,一起剝皮案震驚了整個(gè)濱河市驻债,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌形葬,老刑警劉巖合呐,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異笙以,居然都是意外死亡淌实,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)拆祈,“玉大人恨闪,你說(shuō)我怎么就攤上這事》呕担” “怎么了咙咽?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)淤年。 經(jīng)常有香客問(wèn)我钧敞,道長(zhǎng),這世上最難降的妖魔是什么麸粮? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任溉苛,我火速辦了婚禮,結(jié)果婚禮上弄诲,老公的妹妹穿的比我還像新娘愚战。我一直安慰自己,他們只是感情好威根,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布凤巨。 她就那樣靜靜地躺著,像睡著了一般洛搀。 火紅的嫁衣襯著肌膚如雪敢茁。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,036評(píng)論 1 285
  • 那天留美,我揣著相機(jī)與錄音彰檬,去河邊找鬼。 笑死谎砾,一個(gè)胖子當(dāng)著我的面吹牛逢倍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播景图,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼较雕,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了挚币?” 一聲冷哼從身側(cè)響起亮蒋,我...
    開(kāi)封第一講書(shū)人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎妆毕,沒(méi)想到半個(gè)月后慎玖,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡笛粘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年趁怔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了湿硝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡润努,死狀恐怖关斜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情任连,我是刑警寧澤蚤吹,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站随抠,受9級(jí)特大地震影響裁着,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拱她,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一二驰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧秉沼,春花似錦桶雀、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至敞咧,卻和暖如春棘捣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背休建。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工乍恐, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人测砂。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓茵烈,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親砌些。 傳聞我的和親對(duì)象是個(gè)殘疾皇子呜投,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容