<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          TypeScript 練習(xí)題(第二彈)

          共 23386字,需瀏覽 47分鐘

           ·

          2020-10-20 00:41

          點(diǎn)擊藍(lán)色“腦洞前端”關(guān)注我喲

          加個(gè)“星標(biāo)”,帶你揭開大前端的神秘面紗!

          ?

          這是腦洞前端第「106」篇原創(chuàng)文章

          TypeScript 的學(xué)習(xí)資料非常多,其中也不乏很多優(yōu)秀的文章和教程。但是目前為止沒有一個(gè)我特別滿意的。原因有:

          • 它們大多數(shù)沒有一個(gè)清晰的主線,而是按照 API 組織章節(jié)的,內(nèi)容在「邏輯上」比較零散。
          • 大多是“講是什么,怎么用“,而不是”講為什么,講原理“。
          • 大多數(shù)內(nèi)容比較枯燥,趣味性比較低。都是干巴巴的文字,沒有圖片,缺乏能夠引起強(qiáng)烈共鳴的例子。

          因此我的想法是做一套不同市面上大多數(shù)的 TypeScript 學(xué)習(xí)教程。以人類認(rèn)知的角度思考問題,學(xué)習(xí) TypeScript,通過通俗易懂的例子和圖片來(lái)幫助大家建立 TypeScript 世界觀。

          系列安排:

          ?

          目錄將來(lái)可能會(huì)有所調(diào)整。

          ?

          注意,我的系列文章基本不會(huì)講 API,因此需要你有一定的 TypeScript 使用基礎(chǔ),推薦兩個(gè)學(xué)習(xí)資料。

          • 深入理解 TypeScript[1]
          • TypeScript 官方文檔[2]

          結(jié)合這兩個(gè)資料和我的系列教程,掌握 TypeScript 指日可待。

          接下來(lái),我們通過幾個(gè)方面來(lái)從宏觀的角度來(lái)看一下 TypeScript。

          前言

          本文涉及的題目一共十六道,全部都可以在 typescript-exercises[3] 上在線提交。

          可以和標(biāo)準(zhǔn)答案進(jìn)行對(duì)比。

          并且由于使用了瀏覽器緩存, 因此無(wú)需登錄的情況下也可以保證關(guān)掉頁(yè)面,你的答題進(jìn)度也會(huì)保留。

          ?

          想重置進(jìn)度,清空緩存,無(wú)痕模式或者換瀏覽器都可以。

          ?

          題目中涉及到的知識(shí)點(diǎn)我基本也都在之前的文章中提到了,如果你沒有看過,強(qiáng)烈建議先完成前面的教程,然后將上面的題目自己做一遍之后再看本文。另外一定要按照順序讀, 因此前面的題目都是后面的鋪墊。

          為了不讓文章太過于冗長(zhǎng), 本篇文章分兩次發(fā)布, 一次 8 道題,一共十五道。每道題都有思路,前置知識(shí)以及代碼。「這次給大家?guī)?lái)的是后 6 道」

          ?

          其中有一道題需要大家有函數(shù)式編程的知識(shí), 如果大家不知道會(huì)比較難以解釋。為了避免內(nèi)容太過分散,將這道題從我的題解中移除,故只有 6 道。

          ?

          題目九

          題目描述

          Intro:

          ????PowerUsers?idea?was?bad.?Once?those?users?got
          ????extended?permissions,?they?started?bullying?others
          ????and?we?lost?a?lot?of?great?users.
          ????As?a?response?we?spent?all?the?remaining?money
          ????on?the?marketing?and?got?even?more?users.
          ????We?need?to?start?preparing?to?move?everything?to?a
          ????real?database.?For?now?we?just?do?some?mocks.

          ????The?server?API?format?was?decided?to?be?the?following:

          ????In?case?of?success:?{?status:?'success',?data:?RESPONSE_DATA?}
          ????In?case?of?error:?{?status:?'error',?error:?ERROR_MESSAGE?}

          ????The?API?engineer?started?creating?types?for?this?API?and
          ????quickly?figured?out?that?the?amount?of?types?needed?to?be
          ????created?is?too?big.

          Exercise:

          ????Remove?UsersApiResponse?and?AdminsApiResponse?types
          ????and?use?generic?type?ApiResponse?in?order?to?specify?API
          ????response?formats?for?each?of?the?functions.

          題目的大概意思是:之前都是寫死的數(shù)據(jù), 現(xiàn)在數(shù)據(jù)需要從接口拿,請(qǐng)你定義這個(gè)接口的類型。

          題目?jī)?nèi)置代碼

          interface?User?{
          ??type:?"user";
          ??name:?string;
          ??age:?number;
          ??occupation:?string;
          }

          interface?Admin?{
          ??type:?"admin";
          ??name:?string;
          ??age:?number;
          ??role:?string;
          }

          type?Person?=?User?|?Admin;

          const?admins:?Admin[]?=?[
          ??{?type:?"admin",?name:?"Jane?Doe",?age:?32,?role:?"Administrator"?},
          ??{?type:?"admin",?name:?"Bruce?Willis",?age:?64,?role:?"World?saver"?},
          ];

          const?users:?User[]?=?[
          ??{
          ????type:?"user",
          ????name:?"Max?Mustermann",
          ????age:?25,
          ????occupation:?"Chimney?sweep",
          ??},
          ??{?type:?"user",?name:?"Kate?Müller",?age:?23,?occupation:?"Astronaut"?},
          ];

          export?type?ApiResponse?=?unknown;

          type?AdminsApiResponse?=
          ??|?{
          ??????status:?"success";
          ??????data:?Admin[];
          ????}
          ??|?{
          ??????status:?"error";
          ??????error:?string;
          ????};

          export?function?requestAdmins(callback:?(response:?AdminsApiResponse)?=>?void)?{
          ??callback({
          ????status:?"success",
          ????data:?admins,
          ??});
          }

          type?UsersApiResponse?=
          ??|?{
          ??????status:?"success";
          ??????data:?User[];
          ????}
          ??|?{
          ??????status:?"error";
          ??????error:?string;
          ????};

          export?function?requestUsers(callback:?(response:?UsersApiResponse)?=>?void)?{
          ??callback({
          ????status:?"success",
          ????data:?users,
          ??});
          }

          export?function?requestCurrentServerTime(
          ??callback:?(response:?unknown)?=>?void
          )?
          {
          ??callback({
          ????status:?"success",
          ????data:?Date.now(),
          ??});
          }

          export?function?requestCoffeeMachineQueueLength(
          ??callback:?(response:?unknown)?=>?void
          )?
          {
          ??callback({
          ????status:?"error",
          ????error:?"Numeric?value?has?exceeded?Number.MAX_SAFE_INTEGER.",
          ??});
          }

          function?logPerson(person:?Person)?{
          ??console.log(
          ????`?-?${person.name},?${person.age},?${
          ??????person.type?===?"admin"???person.role?:?person.occupation
          ????}
          `

          ??);
          }

          function?startTheApp(callback:?(error:?Error?|?null)?=>?void)?{
          ??requestAdmins((adminsResponse)?=>?{
          ????console.log("Admins:");
          ????if?(adminsResponse.status?===?"success")?{
          ??????adminsResponse.data.forEach(logPerson);
          ????}?else?{
          ??????return?callback(new?Error(adminsResponse.error));
          ????}

          ????console.log();

          ????requestUsers((usersResponse)?=>?{
          ??????console.log("Users:");
          ??????if?(usersResponse.status?===?"success")?{
          ????????usersResponse.data.forEach(logPerson);
          ??????}?else?{
          ????????return?callback(new?Error(usersResponse.error));
          ??????}

          ??????console.log();

          ??????requestCurrentServerTime((serverTimeResponse)?=>?{
          ????????console.log("Server?time:");
          ????????if?(serverTimeResponse.status?===?"success")?{
          ??????????console.log(
          ????????????`???${new?Date(serverTimeResponse.data).toLocaleString()}`
          ??????????);
          ????????}?else?{
          ??????????return?callback(new?Error(serverTimeResponse.error));
          ????????}

          ????????console.log();

          ????????requestCoffeeMachineQueueLength((coffeeMachineQueueLengthResponse)?=>?{
          ??????????console.log("Coffee?machine?queue?length:");
          ??????????if?(coffeeMachineQueueLengthResponse.status?===?"success")?{
          ????????????console.log(`???${coffeeMachineQueueLengthResponse.data}`);
          ??????????}?else?{
          ????????????return?callback(new?Error(coffeeMachineQueueLengthResponse.error));
          ??????????}

          ??????????callback(null);
          ????????});
          ??????});
          ????});
          ??});
          }

          startTheApp((e:?Error?|?null)?=>?{
          ??console.log();
          ??if?(e)?{
          ????console.log(
          ??????`Error:?"${e.message}",?but?it's?fine,?sometimes?errors?are?inevitable.`
          ????);
          ??}?else?{
          ????console.log("Success!");
          ??}
          });

          前置知識(shí)

          • 泛型
          • 回調(diào)函數(shù)

          思路

          我們還是直接看報(bào)錯(cuò)。

          很明顯這個(gè)報(bào)錯(cuò)的原因是類型是 unknown, 因此我們只有將 unknown 改成正確的類型即可。

          換句話說(shuō), 就是把這種地方改成正確類型即可。

          題目描述說(shuō)了, 這個(gè) response 其實(shí)是從后端返回的。而后端返回的數(shù)據(jù)有固定的格式。比如獲取用戶列表接口:

          type?UsersApiResponse?=
          ??|?{
          ??????status:?"success";
          ??????data:?User[];
          ????}
          ??|?{
          ??????status:?"error";
          ??????error:?string;
          ????};

          其他接口也是類似, 不同的是 data 的類型。因此我們考慮使用泛型封裝,將 data 的類型作為參數(shù)即可。

          從本質(zhì)上來(lái)說(shuō), 就是從后端取的數(shù)據(jù)有兩種大的可能, 一種是錯(cuò)誤, 一種是成功。兩者在同一接口同一時(shí)刻只會(huì)出現(xiàn)一個(gè),且必須出現(xiàn)一個(gè)。

          而成功的情況又會(huì)隨著接口不同從而可能產(chǎn)生不同的類型。

          這是明顯的使用 「或邏輯關(guān)系」「泛型進(jìn)行類型定義」的強(qiáng)烈信號(hào)。我們可以使用泛型做如下改造:

          export?type?ApiResponse?=
          ??|?{
          ??????status:?"success";
          ??????data:?T;
          ????}
          ??|?{
          ??????status:?"error";
          ??????error:?string;
          ????};

          那么上面的 UsersApiResponse 就可以變成:

          type?UsersApiResponse?=?ApiResponse;

          不懂的同學(xué)建議看下我之前的文章:- 你不知道的 TypeScript 泛型(萬(wàn)字長(zhǎng)文,建議收藏)

          用同樣的套路把其他后端返回加上類型即可。

          代碼

          核心代碼:

          export?type?ApiResponse?=
          ??|?{
          ??????status:?"success";
          ??????data:?T;
          ????}
          ??|?{
          ??????status:?"error";
          ??????error:?string;
          ????};

          export?function?requestAdmins(
          ??callback:?(response:?ApiResponse)?=>?void
          )?
          {
          ??callback({
          ????status:?"success",
          ????data:?admins,
          ??});
          }

          export?function?requestUsers(
          ??callback:?(response:?ApiResponse)?=>?void
          )?
          {
          ??callback({
          ????status:?"success",
          ????data:?users,
          ??});
          }

          export?function?requestCurrentServerTime(
          ??callback:?(response:?ApiResponse<number>)?=>?void
          )?
          {
          ??callback({
          ????status:?"success",
          ????data:?Date.now(),
          ??});
          }

          export?function?requestCoffeeMachineQueueLength(
          ??callback:?(response:?ApiResponse<number>)?=>?void
          )?
          {
          ??callback({
          ????status:?"error",
          ????error:?"Numeric?value?has?exceeded?Number.MAX_SAFE_INTEGER.",
          ??});
          }

          題目十

          題目描述

          Intro:

          ????We?have?asynchronous?functions?now,?advanced?technology.
          ????This?makes?us?a?tech?startup?officially?now.
          ????But?one?of?the?consultants?spoiled?our?dreams?about
          ????inevitable?future?IT?leadership.
          ????He?said?that?callback-based?asynchronicity?is?not
          ????popular?anymore?and?everyone?should?use?Promises.
          ????He?promised?that?if?we?switch?to?Promises,?this?would
          ????bring?promising?results.

          Exercise:

          ????We?don't?want?to?reimplement?all?the?data-requesting
          ????functions.?Let'
          s?decorate?the?old?callback-based
          ????functions?with?the?new?Promise-compatible?result.
          ????The?final?function?should?return?a?Promise?which
          ????would?resolve?with?the?final?data?directly
          ????(i.e.?users?or?admins)?or?would?reject?with?an?error
          ????(or?type?Error).

          ????The?function?should?be?named?promisify.

          Higher?difficulty?bonus?exercise:

          ????Create?a?function?promisifyAll?which?accepts?an?object
          ????with?functions?and?returns?a?new?object?where?each?of
          ????the?function?is?promisified.

          ????Rewrite?api?creation?accordingly:

          ????????const?api?=?promisifyAll(oldApi);

          題目大意是:前面用的是基于 callback 形式的代碼, 他們對(duì)代碼進(jìn)行了重構(gòu),改造成了 Promise,讓你對(duì)基于 Promise 的接口進(jìn)行類型定義。

          題目?jī)?nèi)置代碼

          interface?User?{
          ??type:?"user";
          ??name:?string;
          ??age:?number;
          ??occupation:?string;
          }

          interface?Admin?{
          ??type:?"admin";
          ??name:?string;
          ??age:?number;
          ??role:?string;
          }

          type?Person?=?User?|?Admin;

          const?admins:?Admin[]?=?[
          ??{?type:?"admin",?name:?"Jane?Doe",?age:?32,?role:?"Administrator"?},
          ??{?type:?"admin",?name:?"Bruce?Willis",?age:?64,?role:?"World?saver"?},
          ];

          const?users:?User[]?=?[
          ??{
          ????type:?"user",
          ????name:?"Max?Mustermann",
          ????age:?25,
          ????occupation:?"Chimney?sweep",
          ??},
          ??{?type:?"user",?name:?"Kate?Müller",?age:?23,?occupation:?"Astronaut"?},
          ];

          export?type?ApiResponse?=
          ??|?{
          ??????status:?"success";
          ??????data:?T;
          ????}
          ??|?{
          ??????status:?"error";
          ??????error:?string;
          ????};

          export?function?promisify(arg:?unknown):?unknown?{
          ??return?null;
          }

          const?oldApi?=?{
          ??requestAdmins(callback:?(response:?ApiResponse)?=>?void)?{
          ????callback({
          ??????status:?"success",
          ??????data:?admins,
          ????});
          ??},
          ??requestUsers(callback:?(response:?ApiResponse)?=>?void)?{
          ????callback({
          ??????status:?"success",
          ??????data:?users,
          ????});
          ??},
          ??requestCurrentServerTime(callback:?(response:?ApiResponse<number>)?=>?void)?{
          ????callback({
          ??????status:?"success",
          ??????data:?Date.now(),
          ????});
          ??},
          ??requestCoffeeMachineQueueLength(
          ????callback:?(response:?ApiResponse<number>)?=>?void
          ??)?{
          ????callback({
          ??????status:?"error",
          ??????error:?"Numeric?value?has?exceeded?Number.MAX_SAFE_INTEGER.",
          ????});
          ??},
          };

          export?const?api?=?{
          ??requestAdmins:?promisify(oldApi.requestAdmins),
          ??requestUsers:?promisify(oldApi.requestUsers),
          ??requestCurrentServerTime:?promisify(oldApi.requestCurrentServerTime),
          ??requestCoffeeMachineQueueLength:?promisify(
          ????oldApi.requestCoffeeMachineQueueLength
          ??),
          };

          function?logPerson(person:?Person)?{
          ??console.log(
          ????`?-?${person.name},?${person.age},?${
          ??????person.type?===?"admin"???person.role?:?person.occupation
          ????}
          `

          ??);
          }

          async?function?startTheApp()?{
          ??console.log("Admins:");
          ??(await?api.requestAdmins()).forEach(logPerson);
          ??console.log();

          ??console.log("Users:");
          ??(await?api.requestUsers()).forEach(logPerson);
          ??console.log();

          ??console.log("Server?time:");
          ??console.log(
          ????`???${new?Date(await?api.requestCurrentServerTime()).toLocaleString()}`
          ??);
          ??console.log();

          ??console.log("Coffee?machine?queue?length:");
          ??console.log(`???${await?api.requestCoffeeMachineQueueLength()}`);
          }

          startTheApp().then(
          ??()?=>?{
          ????console.log("Success!");
          ??},
          ??(e:?Error)?=>?{
          ????console.log(
          ??????`Error:?"${e.message}",?but?it's?fine,?sometimes?errors?are?inevitable.`
          ????);
          ??}
          );

          前置知識(shí)

          • Promise
          • promisify
          • 泛型
          • 高階函數(shù)

          思路

          題目給了一個(gè) promisefy, 并且類型都是 unknown,不難看出, 它就是想讓我們改造 promisefy 使其不報(bào)錯(cuò), 并能正確推導(dǎo)類型。

          export?function?promisify(arg:?unknown):?unknown?{
          ??return?null;
          }

          我們先不考慮這個(gè)類型怎么寫,先把 promiify 實(shí)現(xiàn)一下再說(shuō)。這需要你有一點(diǎn)高階函數(shù)和 promise 的知識(shí)。由于這不是本文的重點(diǎn),因此不贅述。

          export?function?promisify(fn)?{
          ??return?()?=>
          ????new?Promise((resolve,?reject)?=>?{
          ??????fn((response)?=>?{
          ????????if?(response.status?===?"success")?resolve(response.data);
          ????????else?reject(response.error);
          ??????});
          ????});
          }

          接下來(lái),我們需要給其增加類型簽名。

          這個(gè) fn 實(shí)際上是一個(gè)函數(shù),并且又接受一個(gè) callback 作為參數(shù)。因此大概是這個(gè)樣子:

          ((something)?=?void)?=>?void

          這里的 something 實(shí)際上我們?cè)谏弦还?jié)已經(jīng)解決了,直接套用即可。代碼:

          (callback:?(response:?ApiResponse)?=>?void)?=>?void

          整體代碼大概是:

          export?function?promisify<T>(
          ??fn:?(callback:?(response:?ApiResponse)?=>?void)?=>?void
          ):?()?=>?Promise<T>?
          {
          ??//?上面的實(shí)現(xiàn)
          }

          代碼

          核心代碼:

          export?function?promisify<T>(
          ??fn:?(callback:?(response:?ApiResponse)?=>?void)?=>?void
          ):?()?=>?Promise<T>?
          {
          ??return?()?=>
          ????new?Promise((resolve,?reject)?=>?{
          ??????fn((response)?=>?{
          ????????if?(response.status?===?"success")?resolve(response.data);
          ????????else?reject(response.error);
          ??????});
          ????});
          }

          第十一題

          題目描述

          Intro:

          ????In?order?to?engage?users?in?the?communication?with
          ????each?other?we?have?decided?to?decorate?usernames
          ????in?various?ways.?A?brief?search?led?us?to?a?library
          ????called?"str-utils".?Bad?thing?is?that?it?lacks
          ????TypeScript?declarations.

          Exercise:

          ????Check?str-utils?module?implementation?at:
          ????node_modules/str-utils/index.js
          ????node_modules/str-utils/README.md

          ????Provide?type?declaration?for?that?module?in:
          ????declarations/str-utils/index.d.ts

          ????Try?to?avoid?duplicates?of?type?declarations,
          ????use?type?aliases.

          題目的意思是他們用到了一個(gè)庫(kù) str-utils,這個(gè)庫(kù)的人又沒給我們寫類型定義,于是我們不得不去自己寫(好真實(shí)的例子啊)。

          其實(shí)就是讓我們實(shí)現(xiàn)以下函數(shù)的類型簽名:

          import?{
          ??strReverse,
          ??strToLower,
          ??strToUpper,
          ??strRandomize,
          ??strInvertCase,
          }?from?"str-utils";

          題目?jī)?nèi)置代碼

          //?declarations/str-utils/index.d.js
          declare?module?"str-utils"?{
          ??//?export?const?...
          ??//?export?function?...
          }

          //?index.ts
          import?{
          ??strReverse,
          ??strToLower,
          ??strToUpper,
          ??strRandomize,
          ??strInvertCase,
          }?from?"str-utils";

          interface?User?{
          ??type:?"user";
          ??name:?string;
          ??age:?number;
          ??occupation:?string;
          }

          interface?Admin?{
          ??type:?"admin";
          ??name:?string;
          ??age:?number;
          ??role:?string;
          }

          type?Person?=?User?|?Admin;

          const?admins:?Admin[]?=?[
          ??{?type:?"admin",?name:?"Jane?Doe",?age:?32,?role:?"Administrator"?},
          ??{?type:?"admin",?name:?"Bruce?Willis",?age:?64,?role:?"World?saver"?},
          ??{?type:?"admin",?name:?"Steve",?age:?40,?role:?"Steve"?},
          ??{?type:?"admin",?name:?"Will?Bruces",?age:?30,?role:?"Overseer"?},
          ??{?type:?"admin",?name:?"Superwoman",?age:?28,?role:?"Customer?support"?},
          ];

          const?users:?User[]?=?[
          ??{
          ????type:?"user",
          ????name:?"Max?Mustermann",
          ????age:?25,
          ????occupation:?"Chimney?sweep",
          ??},
          ??{?type:?"user",?name:?"Kate?Müller",?age:?23,?occupation:?"Astronaut"?},
          ??{?type:?"user",?name:?"Moses",?age:?70,?occupation:?"Desert?guide"?},
          ??{?type:?"user",?name:?"Superman",?age:?28,?occupation:?"Ordinary?person"?},
          ??{?type:?"user",?name:?"Inspector?Gadget",?age:?31,?occupation:?"Undercover"?},
          ];

          const?isAdmin?=?(person:?Person):?person?is?Admin?=>?person.type?===?"admin";
          const?isUser?=?(person:?Person):?person?is?User?=>?person.type?===?"user";

          export?const?nameDecorators?=?[
          ??strReverse,
          ??strToLower,
          ??strToUpper,
          ??strRandomize,
          ??strInvertCase,
          ];

          function?logPerson(person:?Person)?{
          ??let?additionalInformation:?string?=?"";
          ??if?(isAdmin(person))?{
          ????additionalInformation?=?person.role;
          ??}
          ??if?(isUser(person))?{
          ????additionalInformation?=?person.occupation;
          ??}
          ??const?randomNameDecorator?=
          ????nameDecorators[Math.round(Math.random()?*?(nameDecorators.length?-?1))];
          ??const?name?=?randomNameDecorator(person.name);
          ??console.log(`?-?${name},?${person.age},?${additionalInformation}`);
          }

          ([]?as?Person[]).concat(users,?admins).forEach(logPerson);

          //?In?case?if?you?are?stuck:
          //?https://www.typescriptlang.org/docs/handbook/modules.html#ambient-modules

          前置知識(shí)

          • 如何給缺乏類型定義的第三方庫(kù)定義類型

          思路

          這個(gè)題目的考點(diǎn)就是「如何給缺乏類型定義的第三方庫(kù)定義類型」

          這個(gè)時(shí)候我們只要新建一個(gè)文件然后加入以下代碼即可。

          declare?module?"str-utils"?{
          ??//?在這里定義類型
          ??//?export?const?...
          ??//?export?function?...
          }

          其中 str-utils 是那個(gè)可惡的沒有類型定義的庫(kù)的名字。

          有了這個(gè)知識(shí),我們的代碼就簡(jiǎn)單了。

          代碼

          declare?module?"str-utils"?{
          ??//?export?const?...
          ??//?export?function?...
          ??export?function?strReverse(s:?string):?string;
          ??export?function?strToLower(s:?string):?string;
          ??export?function?strToUpper(s:?string):?string;
          ??export?function?strRandomize(s:?string):?string;
          ??export?function?strInvertCase(s:?string):?string;
          }

          第十二題

          題目描述

          Intro:

          ????We?have?so?many?users?and?admins?in?the?database!
          ????CEO's?father?Jeff?says?that?we?are?a?BigData
          ????startup?now.?We?have?no?idea?what?it?means,?but
          ????Jeff?says?that?we?need?to?do?some?statistics?and
          ????analytics.

          ????We'
          ve?ran?a?questionnaire?within?the?team?to
          ????figure?out?what?do?we?know?about?statistics.
          ????The?only?person?who?filled?it?was?our?coffee
          ????machine?maintainer.?The?answers?were:

          ?????*?Maximums
          ?????*?Minumums
          ?????*?Medians
          ?????*?Averages

          ????We?found?a?piece?of?code?on?stackoverflow?and
          ????compiled?it?into?a?module?`stats`.?The?bad
          ????thing?is?that?it?lacks?type?declarations.

          Exercise:

          ????Check?stats?module?implementation?at:
          ????node_modules/stats/index.js
          ????node_modules/stats/README.md

          ????Provide?type?declaration?for?that?module?in:
          ????declarations/stats/index.d.ts

          Higher?difficulty?bonus?exercise:

          ????Avoid?duplicates?of?type?declarations.

          題目大概意思是又來(lái)了一個(gè)庫(kù),這個(gè)庫(kù)又沒有寫定義,我們又要自己寫。(真實(shí)++)

          題目?jī)?nèi)置代碼

          //?declartions/stats/index.d.ts
          declare?module?"stats"?{
          ??export?function?getMaxIndex(input:?unknown,?comparator:?unknown):?unknown;
          }

          //?index.ts
          import?{
          ??getMaxIndex,
          ??getMaxElement,
          ??getMinIndex,
          ??getMinElement,
          ??getMedianIndex,
          ??getMedianElement,
          ??getAverageValue,
          }?from?"stats";

          interface?User?{
          ??type:?"user";
          ??name:?string;
          ??age:?number;
          ??occupation:?string;
          }

          interface?Admin?{
          ??type:?"admin";
          ??name:?string;
          ??age:?number;
          ??role:?string;
          }

          const?admins:?Admin[]?=?[
          ??{?type:?"admin",?name:?"Jane?Doe",?age:?32,?role:?"Administrator"?},
          ??{?type:?"admin",?name:?"Bruce?Willis",?age:?64,?role:?"World?saver"?},
          ??{?type:?"admin",?name:?"Steve",?age:?40,?role:?"Steve"?},
          ??{?type:?"admin",?name:?"Will?Bruces",?age:?30,?role:?"Overseer"?},
          ??{?type:?"admin",?name:?"Superwoman",?age:?28,?role:?"Customer?support"?},
          ];

          const?users:?User[]?=?[
          ??{
          ????type:?"user",
          ????name:?"Max?Mustermann",
          ????age:?25,
          ????occupation:?"Chimney?sweep",
          ??},
          ??{?type:?"user",?name:?"Kate?Müller",?age:?23,?occupation:?"Astronaut"?},
          ??{?type:?"user",?name:?"Moses",?age:?70,?occupation:?"Desert?guide"?},
          ??{?type:?"user",?name:?"Superman",?age:?28,?occupation:?"Ordinary?person"?},
          ??{?type:?"user",?name:?"Inspector?Gadget",?age:?31,?occupation:?"Undercover"?},
          ];

          function?logUser(user:?User?|?null)?{
          ??if?(!user)?{
          ????console.log("?-?none");
          ????return;
          ??}
          ??const?pos?=?users.indexOf(user)?+?1;
          ??console.log(`?-?#${pos}?User:?${user.name},?${user.age},?${user.occupation}`);
          }

          function?logAdmin(admin:?Admin?|?null)?{
          ??if?(!admin)?{
          ????console.log("?-?none");
          ????return;
          ??}
          ??const?pos?=?admins.indexOf(admin)?+?1;
          ??console.log(`?-?#${pos}?Admin:?${admin.name},?${admin.age},?${admin.role}`);
          }

          const?compareUsers?=?(a:?User,?b:?User)?=>?a.age?-?b.age;
          const?compareAdmins?=?(a:?Admin,?b:?Admin)?=>?a.age?-?b.age;
          const?colorizeIndex?=?(value:?number)?=>?String(value?+?1);

          export?{
          ??getMaxIndex,
          ??getMaxElement,
          ??getMinIndex,
          ??getMinElement,
          ??getMedianIndex,
          ??getMedianElement,
          ??getAverageValue,
          };

          console.log("Youngest?user:");
          logUser(getMinElement(users,?compareUsers));
          console.log(
          ??`?-?was?${colorizeIndex(getMinIndex(users,?compareUsers))}th?to?register`
          );

          console.log();

          console.log("Median?user:");
          logUser(getMedianElement(users,?compareUsers));
          console.log(
          ??`?-?was?${colorizeIndex(getMedianIndex(users,?compareUsers))}th?to?register`
          );

          console.log();

          console.log("Oldest?user:");
          logUser(getMaxElement(users,?compareUsers));
          console.log(
          ??`?-?was?${colorizeIndex(getMaxIndex(users,?compareUsers))}th?to?register`
          );

          console.log();

          console.log("Average?user?age:");
          console.log(
          ??`?-?${String(getAverageValue(users,?({?age?}:?User)?=>?age))}?years`
          );

          console.log();

          console.log("Youngest?admin:");
          logAdmin(getMinElement(admins,?compareAdmins));
          console.log(
          ??`?-?was?${colorizeIndex(getMinIndex(users,?compareUsers))}th?to?register`
          );

          console.log();

          console.log("Median?admin:");
          logAdmin(getMedianElement(admins,?compareAdmins));
          console.log(
          ??`?-?was?${colorizeIndex(getMedianIndex(users,?compareUsers))}th?to?register`
          );

          console.log();

          console.log("Oldest?admin:");
          logAdmin(getMaxElement(admins,?compareAdmins));
          console.log(
          ??`?-?was?${colorizeIndex(getMaxIndex(users,?compareUsers))}th?to?register`
          );

          console.log();

          console.log("Average?admin?age:");
          console.log(
          ??`?-?${String(getAverageValue(admins,?({?age?}:?Admin)?=>?age))}?years`
          );

          前置知識(shí)

          • 泛型
          • 高階函數(shù)
          • 如何給缺乏類型定義的第三方庫(kù)定義類型

          思路

          和上面的思路類似。唯一的不同的是這道題的需要實(shí)現(xiàn)的幾個(gè)方法支持不同的入?yún)㈩愋汀?/p>

          import?{
          ??getMaxIndex,
          ??getMaxElement,
          ??getMinIndex,
          ??getMinElement,
          ??getMedianIndex,
          ??getMedianElement,
          ??getAverageValue,
          }?from?"stats";

          因此,我們考慮使用泛型來(lái)定義。知道了這個(gè), 代碼就不難寫。這是最最基本的泛型, 比我們前面寫的還簡(jiǎn)單。

          代碼

          declare?module?"stats"?{
          ??export?function?getMaxIndex<T>(
          ????input:?T[],
          ????comparator:?(a:?T,?b:?T)?=>?number
          ??
          ):?number
          ;
          ??export?function?getMaxElement<T>(
          ????input:?T[],
          ????comparator:?(a:?T,?b:?T)?=>?number
          ??
          ):?T
          ;
          ??export?function?getMinElement<T>(
          ????input:?T[],
          ????comparator:?(a:?T,?b:?T)?=>?number
          ??
          ):?T
          ;
          ??export?function?getMedianIndex<T>(
          ????input:?T[],
          ????comparator:?(a:?T,?b:?T)?=>?number
          ??
          ):?number
          ;
          ??export?function?getMedianElement<T>(
          ????input:?T[],
          ????comparator:?(a:?T,?b:?T)?=>?number
          ??
          ):?T
          ;
          ??export?function?getAverageValue<T>(
          ????input:?T[],
          ????getValue:?(a:?T)?=>?number
          ??
          ):?number
          ;
          ??export?function?getMinIndex<T>(
          ????input:?T[],
          ????comparator:?(a:?T,?b:?T)?=>?number
          ??
          ):?number
          ;
          }

          第十三題

          題目描述

          Intro:

          ????The?next?logical?step?for?us?is?to?provide?more
          ????precise?registration?date?for?our?users?and?admins.
          ????We've?approximately?made?up?dates?for?each?user?and
          ????admin?and?used?a?library?called?"date-wizard"?in
          ????order?to?pretty-format?the?dates.

          ????Unfortunately,?type?declarations?which?came?with
          ????"date-wizard"?library?were?incomplete.

          ????1.?DateDetails?interface?is?missing
          ???????time?related?fields?such?as?hours,?minutes?and
          ???????seconds.
          ????2.?Function?"pad"?is?exported?but?not?declared.

          Exercise:

          ????Check?date-wizard?module?implementation?at:
          ????node_modules/date-wizard/index.js
          ????node_modules/date-wizard/index.d.ts

          ????Extend?type?declaration?of?that?module?in:
          ????module-augmentations/date-wizard/index.ts

          題目大概意思是又來(lái)了一個(gè)庫(kù),這個(gè)庫(kù)又沒有寫定義,我們又要自己寫。(真實(shí)+++++++++++++)

          題目?jī)?nèi)置代碼

          //?module-augmentations/data-wizard/index.d.ts

          //?This?enables?module?augmentation?mode.
          import?"date-wizard";

          declare?module?"date-wizard"?{
          ??//?Add?your?module?extensions?here.
          }

          //?index.ts
          import?*?as?dateWizard?from?"date-wizard";
          import?"./module-augmentations/date-wizard";

          interface?User?{
          ??type:?"user";
          ??name:?string;
          ??age:?number;
          ??occupation:?string;
          ??registered:?Date;
          }

          interface?Admin?{
          ??type:?"admin";
          ??name:?string;
          ??age:?number;
          ??role:?string;
          ??registered:?Date;
          }

          type?Person?=?User?|?Admin;

          const?admins:?Admin[]?=?[
          ??{
          ????type:?"admin",
          ????name:?"Jane?Doe",
          ????age:?32,
          ????role:?"Administrator",
          ????registered:?new?Date("2016-06-01T16:23:13"),
          ??},
          ??{
          ????type:?"admin",
          ????name:?"Bruce?Willis",
          ????age:?64,
          ????role:?"World?saver",
          ????registered:?new?Date("2017-02-11T12:12:11"),
          ??},
          ??{
          ????type:?"admin",
          ????name:?"Steve",
          ????age:?40,
          ????role:?"Steve",
          ????registered:?new?Date("2018-01-05T11:02:30"),
          ??},
          ??{
          ????type:?"admin",
          ????name:?"Will?Bruces",
          ????age:?30,
          ????role:?"Overseer",
          ????registered:?new?Date("2018-08-12T10:01:24"),
          ??},
          ??{
          ????type:?"admin",
          ????name:?"Superwoman",
          ????age:?28,
          ????role:?"Customer?support",
          ????registered:?new?Date("2019-03-25T07:51:05"),
          ??},
          ];

          const?users:?User[]?=?[
          ??{
          ????type:?"user",
          ????name:?"Max?Mustermann",
          ????age:?25,
          ????occupation:?"Chimney?sweep",
          ????registered:?new?Date("2016-02-15T09:25:13"),
          ??},
          ??{
          ????type:?"user",
          ????name:?"Kate?Müller",
          ????age:?23,
          ????occupation:?"Astronaut",
          ????registered:?new?Date("2016-03-23T12:47:03"),
          ??},
          ??{
          ????type:?"user",
          ????name:?"Moses",
          ????age:?70,
          ????occupation:?"Desert?guide",
          ????registered:?new?Date("2017-02-19T17:22:56"),
          ??},
          ??{
          ????type:?"user",
          ????name:?"Superman",
          ????age:?28,
          ????occupation:?"Ordinary?person",
          ????registered:?new?Date("2018-02-25T19:44:28"),
          ??},
          ??{
          ????type:?"user",
          ????name:?"Inspector?Gadget",
          ????age:?31,
          ????occupation:?"Undercover",
          ????registered:?new?Date("2019-03-25T09:29:12"),
          ??},
          ];

          const?isAdmin?=?(person:?Person):?person?is?Admin?=>?person.type?===?"admin";
          const?isUser?=?(person:?Person):?person?is?User?=>?person.type?===?"user";

          function?logPerson(person:?Person,?index:?number)?{
          ??let?additionalInformation:?string?=?"";
          ??if?(isAdmin(person))?{
          ????additionalInformation?=?person.role;
          ??}
          ??if?(isUser(person))?{
          ????additionalInformation?=?person.occupation;
          ??}
          ??let?registeredAt?=?dateWizard(
          ????person.registered,
          ????"{date}.{month}.{year}?{hours}:{minutes}"
          ??);
          ??let?num?=?`#${dateWizard.pad(index?+?1)}`;
          ??console.log(
          ????`?-?${num}:?${person.name},?${person.age},?${additionalInformation},?${registeredAt}`
          ??);
          }

          export?{?dateWizard?};

          console.log("All?users:");

          ([]?as?Person[]).concat(users,?admins).forEach(logPerson);

          console.log();

          console.log("Early?birds:");

          ([]?as?Person[])
          ??.concat(users,?admins)
          ??.filter((person)?=>?dateWizard.dateDetails(person.registered).hours?10)
          ??.forEach(logPerson);

          //?In?case?if?you?are?stuck:
          //?https://www.typescriptlang.org/docs/handbook/modules.html#ambient-modules
          //?https://www.typescriptlang.org/docs/handbook/declaration-merging.html

          前置知識(shí)

          • interface 或 type 聲明自定義類型
          • 如何給缺乏類型定義的第三方庫(kù)定義類型

          思路

          和上面兩道題思路一樣, 不用多說(shuō)了吧?

          代碼

          //?This?enables?module?augmentation?mode.
          import?"date-wizard";

          declare?module?"date-wizard"?{
          ??//?Add?your?module?extensions?here.
          ??function?dateWizard(date:?string,?format:?string):?string;
          ??function?pad(s:?number):?string;
          ??interface?DateDetails?{
          ????year:?number;
          ????month:?number;
          ????date:?number;
          ????hours:?number;
          ????minutes:?number;
          ????seconds:?number;
          ??}
          ??function?dateDetails(date:?Date):?DateDetails;
          }

          第十四題

          需要大家有函數(shù)式編程的知識(shí), 如果大家不知道會(huì)比較難以解釋。為了避免內(nèi)容太過分散,將這道題從我的題解中移除。

          對(duì)函數(shù)式編程感興趣的,也可是看下我之前寫的文章 函數(shù)式編程系列教程。

          第十五題

          題目描述

          Intro:

          ????Our?attempt?to?Open?Source?didn't?work?quite?as
          ????expected.?It?turned?out?there?were?already?many
          ????existing?functional?JS?libraries.

          ????All?the?remaining?developers?left?the?company?as
          ????well.?It?seems?that?they?are?joining?a?very
          ????ambitious?startup?which?re-invented?a?juicer?and
          ????raised?millions?of?dollars.
          ????Too?bad?we?cannot?compete?with?this?kind?of
          ????financing?even?though?we?believe?our?idea?is
          ????great.

          ????It'
          s?time?to?shine?for?the?last?time?and?publish
          ????our?new?invention:?object-constructor?as?our?CTO
          ????named?it.?A?small?library?which?helps
          ????manipulating?an?object.

          Exercise:

          ????Here?is?a?library?which?helps?manipulating?objects.
          ????We?tried?to?write?type?annotations?and?we?failed.
          ????Please?help!

          題目大概意思是函數(shù)式編程他們 hold 不住,于是又準(zhǔn)備切換到面向?qū)ο缶幊獭S谑悄阈枰a(bǔ)充類型定義使得代碼不報(bào)錯(cuò)。

          題目?jī)?nèi)置代碼

          export?class?ObjectManipulator?{
          ??constructor(protected?obj)?{}

          ??public?set(key,?value)?{
          ????return?new?ObjectManipulator({?...this.obj,?[key]:?value?});
          ??}

          ??public?get(key)?{
          ????return?this.obj[key];
          ??}

          ??public?delete(key)?{
          ????const?newObj?=?{?...this.obj?};
          ????delete?newObj[key];
          ????return?new?ObjectManipulator(newObj);
          ??}

          ??public?getObject()?{
          ????return?this.obj;
          ??}
          }

          前置知識(shí)

          • 泛型
          • Omit 泛型
          • ES6 class
          • keyof
          • 使用 extends 進(jìn)行泛型約束
          • 聯(lián)合類型

          思路

          這道題難度頗高,比前面的泛型題目都要難。也是本系列的壓軸題,我們重點(diǎn)講一下。

          首先題目有五個(gè)報(bào)錯(cuò)位置, 報(bào)錯(cuò)信息都是隱式使用了 any , 因此我們的思路就是將五個(gè)地方顯式聲明類型即可。

          從它的名字 ObjectManipulator 以及 api 可以看出, 它應(yīng)該可以存儲(chǔ)任何對(duì)象,因此使用泛型定義就不難想到。

          你也可是把這個(gè) ObjectManipulator 想象成抽象包包。你的期望是限量款包包拍照的時(shí)候用,普通包包和閨蜜逛街的時(shí)候用,優(yōu)衣庫(kù)送的包包逛超市的時(shí)候用等等。

          ObjectManipulator 是一個(gè)抽象的包包概念,不是具體的包, 比如當(dāng)你買一個(gè) LV 的包包的時(shí)候就是 ObjectManipulator。這樣當(dāng)你往 LV 里放超市買的水果的時(shí)候就可以報(bào)錯(cuò):你怎么可以用 LV 包包裝這樣?xùn)|西呢?你應(yīng)該用 ta 裝*

          ?

          當(dāng)然這個(gè)例子很不嚴(yán)謹(jǐn), 這個(gè)只是幫助大家快速理解而已,切莫較真。

          ?

          理解了題意,我們就可以開始寫了。

          我們先改第一個(gè)錯(cuò) - 構(gòu)造函數(shù) constructor, 這個(gè)錯(cuò)比較簡(jiǎn)單。

          export?class?ObjectManipulator?{
          ??constructor(protected?obj:?T)?{
          ????this.obj?=?obj;
          ??}
          ??...
          }

          這個(gè)時(shí)候經(jīng)過 ObjectManipulator 實(shí)例化產(chǎn)生的對(duì)象的 this.obj 都是 T 類型,其中 T 是泛型。因此 getObject 的錯(cuò)也不難改,返回值寫 T 就行。

          export?class?ObjectManipulator?{
          ??...
          ??public?getObject():?T?{
          ????return?this.obj;
          ??}
          }

          剩下的 get,set 和 delete 思路有點(diǎn)類似。先拿 get 來(lái)說(shuō):

          export?class?ObjectManipulator?{
          ??...
          ??public?get(key)?{
          ????return?this.obj[key];
          ??}
          ??...
          }

          這個(gè)怎么寫類型呢?key 理論上可是是任何值,返回值理論上也可以是任何值。但是一旦類型 T 確定了, 那么實(shí)際上 key 和返回值就不是任意值了。比如:

          type?A?=?ObjectManipulator<{?name:?string;?age:?number?}>;
          const?a:?A?=?new?ObjectManipulator({?name:?"",?age:?17?});

          如上代碼中的 A 是 ObjectManipulator 傳入具體類型 { name: string; age: number } 產(chǎn)生的新的類型。

          ?

          我這里用的是行內(nèi)類型, 實(shí)際項(xiàng)目建議使用 interface 或者 type 定義類型。

          ?

          之后我們模擬一些操作:

          a.set("name",?"腦洞前端");
          a.get("name");
          a.get("name123");?//?期望報(bào)錯(cuò)
          a.set("name123",?"腦洞");
          a.delete("name123");?//?期望報(bào)錯(cuò)
          a.delete("name");

          實(shí)際上,我「可能」期望的是其中一些行為可以借助 TypeScript 的類型分析直接報(bào)錯(cuò)。

          簡(jiǎn)單來(lái)說(shuō),我的期望是 「get 和 delete 不在 T 中的 key 都報(bào)錯(cuò)。」

          ?

          當(dāng)然你的真實(shí)項(xiàng)目也可以不認(rèn)同我的觀點(diǎn), 比如 get 一個(gè)不在 T 中定義的 key 也可以,但是我還是推薦你這么做。

          ?

          知道了這個(gè), 再結(jié)合我之前有關(guān)泛型的文章就不難寫出來(lái)。

          其中 get 和 delete 的代碼:

          export?class?ObjectManipulator?{
          ??public?getextends?keyof?T>(key:?K):?T[K]?{
          ????return?this.obj[key];
          ??}

          ??public?deleteextends?keyof?T>(key:?K):?ObjectManipulator>?{
          ????const?newObj?=?{?...this.obj?};
          ????delete?newObj[key];
          ????return?new?ObjectManipulator(newObj);
          ??}
          }

          最后是 set,其實(shí)一開始我的 set 是這么寫的。

          export?class?ObjectManipulator?{
          ??public?setextends?keyof?T,?V>(key:?K,?value:?V):?ObjectManipulator?{
          ????return?new?ObjectManipulator({
          ??????...this.obj,
          ??????[key]:?value,
          ????})?as?ObjectManipulatorin?K]:?V?}>;
          ??}
          }

          但是無(wú)奈沒有通過官方的測(cè)試用例。實(shí)際項(xiàng)目我其實(shí)更推薦我上面的這種寫法。下面是我為了通過所有的測(cè)試用例寫的方法。

          經(jīng)過分析, 我發(fā)現(xiàn)它期望的是 set 中的 key 可以不是 T 中的。這一點(diǎn)從官方給的測(cè)試用例就可以看出來(lái)。

          因此我將代碼改成 K 放寬到任意 string,返回值做了一個(gè)聯(lián)合類型。代碼:

          export?class?ObjectManipulator?{
          ??...
          ??public?setextends?string,?V>(
          ????key:?K,
          ????value:?V
          ??):?ObjectManipulatorin?K]:?V?}>?{
          ????return?new?ObjectManipulator({
          ??????...this.obj,
          ??????[key]:?value,
          ????})?as?ObjectManipulatorin?K]:?V?}>;
          ??}
          ??...
          }

          終于通過了所有的測(cè)試用例。

          代碼

          export?class?ObjectManipulator?{
          ??constructor(protected?obj:?T)?{
          ????this.obj?=?obj;
          ??}
          ??public?setextends?string,?V>(
          ????key:?K,
          ????value:?V
          ??):?ObjectManipulatorin?K]:?V?}>?{
          ????return?new?ObjectManipulator({
          ??????...this.obj,
          ??????[key]:?value,
          ????})?as?ObjectManipulatorin?K]:?V?}>;
          ??}

          ??public?getextends?keyof?T>(key:?K):?T[K]?{
          ????return?this.obj[key];
          ??}

          ??public?deleteextends?keyof?T>(key:?K):?ObjectManipulator>?{
          ????const?newObj?=?{?...this.obj?};
          ????delete?newObj[key];
          ????return?new?ObjectManipulator(newObj);
          ??}

          ??public?getObject():?T?{
          ????return?this.obj;
          ??}
          }

          總結(jié)

          以上就是給大家?guī)?lái)的題目解析。這六道題的考點(diǎn)有,按照我個(gè)人理解的重要程度劃分為:

          • type 和 interface 的基本操作(必須掌握)
          • 如何給缺乏類型定義的第三方庫(kù)定義類型(必須掌握)
          • 聯(lián)合類型 和 交叉類型(強(qiáng)烈建議掌握)
          • 類型斷言和類型收縮(強(qiáng)烈建議掌握)
          • 泛型和常見內(nèi)置泛型(強(qiáng)烈建議掌握)
          • 高階函數(shù)的類型定義(強(qiáng)烈建議掌握)

          最后祝愿大家告別 anyscript,成為 TypeScript 魔法師。

          關(guān)注我

          大家也可以關(guān)注我的公眾號(hào)《腦洞前端》獲取更多更新鮮的前端硬核文章,帶你認(rèn)識(shí)你不知道的前端。


          點(diǎn)關(guān)注,不迷路!

          Reference

          [1]

          深入理解 TypeScript: https://jkchao.github.io/typescript-book-chinese/

          [2]

          TypeScript 官方文檔: https://www.typescriptlang.org/docs/home

          [3]

          typescript-exercises: https://typescript-exercises.github.io/


          瀏覽 52
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久国产精品国产色婷婷 | 免费视频在线观看久 | 中国婬乱a一级毛多女 | 无码1234| 免费小黄片视频 |