TypeScript 練習(xí)題
點(diǎn)擊藍(lán)色“腦洞前端”關(guān)注我喲
加個“星標(biāo)”,帶你揭開大前端的神秘面紗!
?這是腦洞前端第「105」篇原創(chuàng)文章
TypeScript 的學(xué)習(xí)資料非常多,其中也不乏很多優(yōu)秀的文章和教程。但是目前為止沒有一個我特別滿意的。原因有:
它們大多數(shù)沒有一個清晰的主線,而是按照 API 組織章節(jié)的,內(nèi)容在「邏輯上」比較零散。 大多是“講是什么,怎么用“,而不是”講為什么,講原理“。 大多數(shù)內(nèi)容比較枯燥,趣味性比較低。都是干巴巴的文字,沒有圖片,缺乏能夠引起強(qiáng)烈共鳴的例子。
因此我的想法是做一套不同市面上大多數(shù)的 TypeScript 學(xué)習(xí)教程。以人類認(rèn)知的角度思考問題,學(xué)習(xí) TypeScript,通過通俗易懂的例子和圖片來幫助大家建立 TypeScript 世界觀。
系列安排:
上帝視角看 TypeScript TypeScript 類型系統(tǒng) types 和 @types 是什么? 你不知道的 TypeScript 泛型(萬字長文,建議收藏) typeScript 配置文件該怎么寫? TypeScript 是如何與 React,Vue,Webpack 集成的? TypeScript 練習(xí)題(就是本文)
?目錄將來可能會有所調(diào)整。
?
注意,我的系列文章基本不會講 API,因此需要你有一定的 TypeScript 使用基礎(chǔ),推薦兩個學(xué)習(xí)資料。
深入理解 TypeScript[1] TypeScript 官方文檔[2]
結(jié)合這兩個資料和我的系列教程,掌握 TypeScript 指日可待。
接下來,我們通過幾個方面來從宏觀的角度來看一下 TypeScript。
前言
本文涉及的題目一共十六道,全部都可以在 typescript-exercises[3] 上在線提交。

可以和標(biāo)準(zhǔn)答案進(jìn)行對比。
并且由于使用了瀏覽器緩存, 因此無需登錄的情況下也可以保證關(guān)掉頁面,你的答題進(jìn)度也會保留。
?想重置進(jìn)度,清空緩存,無痕模式或者換瀏覽器都可以。
?
題目中涉及到的知識點(diǎn)我基本也都在之前的文章中提到了,如果你沒有看過,強(qiáng)烈建議先完成前面的教程,然后將上面的題目自己做一遍之后再看本文。
為了不讓文章太過于冗長, 本篇文章分兩次發(fā)布, 一次 8 道題,一共十六道。每道題都有思路,前置知識以及代碼。
題目一
題目描述
Intro:
????We?are?starting?a?small?community?of?users.?For?performance
????reasons?we?have?decided?to?store?all?users?right?in?the?code.
????This?way?we?can?provide?our?developers?with?more
????user-interaction?opportunities.?With?user-related?data,?at?least.
????All?the?GDPR-related?issues?we?will?solved?some?other?day.
????This?would?be?the?base?for?our?future?experiments?during
????these?exercises.
Exercise:
????Given?the?data,?define?the?interface?"User"?and?use?it?accordingly.
題目的大概意思是讓你定義一個類型 User, 使得代碼可以正常運(yùn)行。
題目內(nèi)置代碼
export?type?User?=?unknown;
export?const?users:?unknown[]?=?[
??{
????name:?"Max?Mustermann",
????age:?25,
????occupation:?"Chimney?sweep",
??},
??{
????name:?"Kate?Müller",
????age:?23,
????occupation:?"Astronaut",
??},
];
export?function?logPerson(user:?unknown)?{
??console.log(`?-?${user.name},?${user.age}`);
}
console.log("Users:");
users.forEach(logPerson);
前置知識
interface 或 type 聲明自定義類型
思路
這道題比較簡單, 我們只有定義一個 User 類即可。從 users 數(shù)組中不難看出, User 中有三個屬性 name ,age 和 occupation,類型分別為 string, number 和 string。因此直接使用 type 或者 interface 定義自定義類型即可。
代碼
核心代碼:
export?type?User?=?{
??name:?string;
??age:?number;
??occupation:?string;
};
題目二
題目描述
Intro:
????All?2?users?liked?the?idea?of?the?community.?We?should?go
????forward?and?introduce?some?order.?We?are?in?Germany?after?all.
????Let's?add?a?couple?of?admins.
????Initially?we?only?had?users?in?the?in-memory?database.?After
????introducing?Admins,?we?need?to?fix?the?types?so?that
????everything?works?well?together.
Exercise:
????Type?"Person"?is?missing,?please?define?it?and?use
????it?in?persons?array?and?logPerson?function?in?order?to?fix
????all?the?TS?errors.
題目大意是補(bǔ)充 Person 類, 使得代碼不報錯。
題目內(nèi)置代碼
interface?User?{
??name:?string;
??age:?number;
??occupation:?string;
}
interface?Admin?{
??name:?string;
??age:?number;
??role:?string;
}
export?type?Person?=?unknown;
export?const?persons:?User[]?/*?<-?Person[]?*/?=?[
??{
????name:?"Max?Mustermann",
????age:?25,
????occupation:?"Chimney?sweep",
??},
??{
????name:?"Jane?Doe",
????age:?32,
????role:?"Administrator",
??},
??{
????name:?"Kate?Müller",
????age:?23,
????occupation:?"Astronaut",
??},
??{
????name:?"Bruce?Willis",
????age:?64,
????role:?"World?saver",
??},
];
export?function?logPerson(user:?User)?{
??console.log(`?-?${user.name},?${user.age}`);
}
persons.forEach(logPerson);
前置知識
聯(lián)合類型
思路
我們直接從報錯入手。

不難發(fā)現(xiàn) persons 數(shù)組既有 User 又有 Admin。因此 person 的函數(shù)簽名應(yīng)該是兩者的聯(lián)合類型。而題目又讓我們補(bǔ)充 Person,于是代碼將 Person 定義為 Admin 和 User 的聯(lián)合類型就不難想到。
代碼
核心代碼:
export?type?Person?=?User?|?Admin;
這個時候, persons 數(shù)組使用的過程只能用 User 和 Admin 的共有屬性, 也就是 name 和 age,這點(diǎn)后面的題目也會提到。因此如果你使用了 role 或者 occupation 就會報錯。怎么解決呢?我們繼續(xù)看下一題。
第三題
題目描述
Intro:
????Since?we?already?have?some?of?the?additional
????information?about?our?users,?it's?a?good?idea
????to?output?it?in?a?nice?way.
Exercise:
????Fix?type?errors?in?logPerson?function.
????logPerson?function?should?accept?both?User?and?Admin
????and?should?output?relevant?information?according?to
????the?input:?occupation?for?User?and?role?for?Admin.
題目內(nèi)置代碼
interface?User?{
??name:?string;
??age:?number;
??occupation:?string;
}
interface?Admin?{
??name:?string;
??age:?number;
??role:?string;
}
export?type?Person?=?User?|?Admin;
export?const?persons:?Person[]?=?[
??{
????name:?"Max?Mustermann",
????age:?25,
????occupation:?"Chimney?sweep",
??},
??{
????name:?"Jane?Doe",
????age:?32,
????role:?"Administrator",
??},
??{
????name:?"Kate?Müller",
????age:?23,
????occupation:?"Astronaut",
??},
??{
????name:?"Bruce?Willis",
????age:?64,
????role:?"World?saver",
??},
];
export?function?logPerson(person:?Person)?{
??let?additionalInformation:?string;
??if?(person.role)?{
????additionalInformation?=?person.role;
??}?else?{
????additionalInformation?=?person.occupation;
??}
??console.log(`?-?${person.name},?${person.age},?${additionalInformation}`);
}
persons.forEach(logPerson);
前置知識
類型斷言 類型收斂 in 操作符
思路
關(guān)于類型收斂, 我在 TypeScript 類型系統(tǒng) 做了很詳情的討論。
上面代碼報錯的原因前面已經(jīng)講過了, 那么如何解決呢?由于 person 可能是 User ,也可能是 Admin 類型,而 TypeScript 沒有足夠的信息確定具體是哪一種。因此你使用 User 或者 Admin 特有的屬性就會報錯了。
因此解決方案的基本思想就是告訴 TypeScript 「person 當(dāng)前是 Admin 還是 User 類型」。有多種方式可以解決這個問題。
將 person 斷言為準(zhǔn)確的類型。就是告訴 TypeScript ”交給我吧, person 就是 xxx 類型,有錯就我的鍋“。
代碼:
if?((person).role)?{
??additionalInformation?=?(person).role;
}?else?{
??additionalInformation?=?(person).occupation;
}
另外一種方式是使用類型收縮,比如 is , in, typeof , instanceof 等。使得 Typescript 能夠 Get 到當(dāng)前的類型。”哦, person 上有 role 屬性啊,那它就是 Admin 類型,有問題我 Typescript 的鍋“
這里我們使用 in 操作符,寫起來也很簡單。
?推薦哪種不用我多說了吧 ?
?
代碼
if?("role"?in?person)?{
??//?person?會被自動推導(dǎo)為?Admin
??additionalInformation?=?person.role;
}?else?{
??//?Person?會被自動推導(dǎo)為?User
??additionalInformation?=?person.occupation;
}
第四題
題目描述
Intro:
????As?we?introduced?"type"?to?both?User?and?Admin
????it's?now?easier?to?distinguish?between?them.
????Once?object?type?checking?logic?was?extracted
????into?separate?functions?isUser?and?isAdmin?-
????logPerson?function?got?new?type?errors.
Exercise:
????Figure?out?how?to?help?TypeScript?understand?types?in
????this?situation?and?apply?necessary?fixes.
大概意思還是讓你改代碼, 使得 Typescript 能理解(不報錯)。
題目內(nèi)置代碼
interface?User?{
??type:?"user";
??name:?string;
??age:?number;
??occupation:?string;
}
interface?Admin?{
??type:?"admin";
??name:?string;
??age:?number;
??role:?string;
}
export?type?Person?=?User?|?Admin;
export?const?persons:?Person[]?=?[
??{
????type:?"user",
????name:?"Max?Mustermann",
????age:?25,
????occupation:?"Chimney?sweep",
??},
??{?type:?"admin",?name:?"Jane?Doe",?age:?32,?role:?"Administrator"?},
??{?type:?"user",?name:?"Kate?Müller",?age:?23,?occupation:?"Astronaut"?},
??{?type:?"admin",?name:?"Bruce?Willis",?age:?64,?role:?"World?saver"?},
];
export?function?isAdmin(person:?Person)?{
??return?person.type?===?"admin";
}
export?function?isUser(person:?Person)?{
??return?person.type?===?"user";
}
export?function?logPerson(person:?Person)?{
??let?additionalInformation:?string?=?"";
??if?(isAdmin(person))?{
????additionalInformation?=?person.role;
??}
??if?(isUser(person))?{
????additionalInformation?=?person.occupation;
??}
??console.log(`?-?${person.name},?${person.age},?${additionalInformation}`);
}
console.log("Admins:");
persons.filter(isAdmin).forEach(logPerson);
console.log();
console.log("Users:");
persons.filter(isUser).forEach(logPerson);
前置知識
類型收斂 is 操作符
思路
我們?nèi)匀粡膱箦e入手。

實(shí)際上還是 person 的類型問題, 沒有被收縮到正確的類型。看題目的代碼,期望效果應(yīng)該是如果進(jìn)入 isAdmin 內(nèi)部,那么 person 就是 Admin 類型,同理進(jìn)入 isUser 內(nèi)部,那么 person 就是 User 類型。
繼續(xù)看下 isAdmin 和 isUser 的實(shí)現(xiàn):
export?function?isAdmin(person:?Person)?{
??return?person.type?===?"admin";
}
export?function?isUser(person:?Person)?{
??return?person.type?===?"user";
}
這里我們期望的效果是如果 isAdmin 函數(shù)返回 true ,那么 person 就應(yīng)該被收斂為 Admin,isUser 同理。
這里就需要用到 is 操作符。
?上文提到了類型收斂常見的操作符是 is , in, typeof , instanceof
?
代碼
export?function?isAdmin(person:?Person):?person?is?Admin?{
??return?person.type?===?"admin";
}
export?function?isUser(person:?Person):?person?is?User?{
??return?person.type?===?"user";
}
這樣當(dāng) isAdmin 返回 true, 那么 person 變量就會被推導(dǎo)成 Admin 類型,而不是聯(lián)合類型, 也就是類型發(fā)生了收縮。
不難看出,這樣的類型斷言會直接影響到調(diào)用 isAdmin 或 isUser 的「函數(shù)的入?yún)⒌念愋汀?/strong>。
第五題
題目描述
Intro:
????Time?to?filter?the?data!?In?order?to?be?flexible
????we?filter?users?using?a?number?of?criteria?and
????return?only?those?matching?all?of?the?criteria.
????We?don't?need?Admins?yet,?we?only?filter?Users.
Exercise:
????Without?duplicating?type?structures,?modify
????filterUsers?function?definition?so?that?we?can
????pass?only?those?criteria?which?are?needed,
????and?not?the?whole?User?information?as?it?is
????required?now?according?to?typing.
Higher?difficulty?bonus?exercise:
????Exclude?"type"?from?filter?criterias.
大概意思是讓你改 filterUsers, 但要注意 DRY(Don't Repeat Yourself)。
題目內(nèi)置代碼
interface?User?{
??type:?"user";
??name:?string;
??age:?number;
??occupation:?string;
}
interface?Admin?{
??type:?"admin";
??name:?string;
??age:?number;
??role:?string;
}
export?type?Person?=?User?|?Admin;
export?const?persons:?Person[]?=?[
??{
????type:?"user",
????name:?"Max?Mustermann",
????age:?25,
????occupation:?"Chimney?sweep",
??},
??{
????type:?"admin",
????name:?"Jane?Doe",
????age:?32,
????role:?"Administrator",
??},
??{
????type:?"user",
????name:?"Kate?Müller",
????age:?23,
????occupation:?"Astronaut",
??},
??{
????type:?"admin",
????name:?"Bruce?Willis",
????age:?64,
????role:?"World?saver",
??},
??{
????type:?"user",
????name:?"Wilson",
????age:?23,
????occupation:?"Ball",
??},
??{
????type:?"admin",
????name:?"Agent?Smith",
????age:?23,
????role:?"Administrator",
??},
];
export?const?isAdmin?=?(person:?Person):?person?is?Admin?=>
??person.type?===?"admin";
export?const?isUser?=?(person:?Person):?person?is?User?=>
??person.type?===?"user";
export?function?logPerson(person:?Person)?{
??let?additionalInformation?=?"";
??if?(isAdmin(person))?{
????additionalInformation?=?person.role;
??}
??if?(isUser(person))?{
????additionalInformation?=?person.occupation;
??}
??console.log(`?-?${person.name},?${person.age},?${additionalInformation}`);
}
export?function?filterUsers(persons:?Person[],?criteria:?User):?User[]?{
??return?persons.filter(isUser).filter((user)?=>?{
????const?criteriaKeys?=?Object.keys(criteria)?as?(keyof?User)[];
????return?criteriaKeys.every((fieldName)?=>?{
??????return?user[fieldName]?===?criteria[fieldName];
????});
??});
}
console.log("Users?of?age?23:");
filterUsers(persons,?{
??age:?23,
}).forEach(logPerson);
前置知識
泛型 Partial 泛型
思路
老規(guī)矩, 從報錯入手。

大概意思是 { age: 23 } 不完整,缺失了部分 key。而題目實(shí)際上的想法應(yīng)該是想根據(jù)部分內(nèi)容對人員進(jìn)行檢錯。比如可以根據(jù) age 查, 也可以根據(jù) name 查,也可以同時根據(jù) age 和 name 查等,這和我們平時的搜索邏輯是一致的。
直接用 Partial 泛型即可解決, 不懂的可以看下我的文章你不知道的 TypeScript 泛型(萬字長文,建議收藏)。
代碼
export?function?filterUsers(persons:?Person[],?criteria:?Partial ):?User[]?{
????...
}
第六題
題目描述
Intro:
????Filtering?requirements?have?grown.?We?need?to?be
????able?to?filter?any?kind?of?Persons.
Exercise:
????Fix?typing?for?the?filterPersons?so?that?it?can?filter?users
????and?return?User[]?when?personType='user'?and?return?Admin[]
????when?personType='admin'.?Also?filterPersons?should?accept
????partial?User/Admin?type?according?to?the?personType.
????`criteria`?argument?should?behave?according?to?the
????`personType`?argument?value.?`type`?field?is?not?allowed?in
????the?`criteria`?field.
Higher?difficulty?bonus?exercise:
????Implement?a?function?`getObjectKeys()`?which?returns?more
????convenient?result?for?any?argument?given,?so?that?you?don't
????need?to?cast?it.
????let?criteriaKeys?=?Object.keys(criteria)?as?(keyof?User)[];
????-->
????let?criteriaKeys?=?getObjectKeys(criteria);
大概意思是讓你改 filterUsers, 但要注意 DRY(Don't Repeat Yourself)。并且可以根據(jù) personType 的不同,返回不同的類型。
題目內(nèi)置代碼
interface?User?{
??type:?"user";
??name:?string;
??age:?number;
??occupation:?string;
}
interface?Admin?{
??type:?"admin";
??name:?string;
??age:?number;
??role:?string;
}
export?type?Person?=?User?|?Admin;
export?const?persons:?Person[]?=?[
??{
????type:?"user",
????name:?"Max?Mustermann",
????age:?25,
????occupation:?"Chimney?sweep",
??},
??{?type:?"admin",?name:?"Jane?Doe",?age:?32,?role:?"Administrator"?},
??{?type:?"user",?name:?"Kate?Müller",?age:?23,?occupation:?"Astronaut"?},
??{?type:?"admin",?name:?"Bruce?Willis",?age:?64,?role:?"World?saver"?},
??{?type:?"user",?name:?"Wilson",?age:?23,?occupation:?"Ball"?},
??{?type:?"admin",?name:?"Agent?Smith",?age:?23,?role:?"Anti-virus?engineer"?},
];
export?function?logPerson(person:?Person)?{
??console.log(
????`?-?${person.name},?${person.age},?${
??????person.type?===?"admin"???person.role?:?person.occupation
????}`
??);
}
export?function?filterPersons(
??persons:?Person[],
??personType:?"admin",
??criteria:?Partial
):?Admin[];
export?function?filterPersons(
??persons:?Person[],
??personType:?"user",
??criteria:?Partial
):?User[];
export?function?filterPersons(
??persons:?Person[],
??personType:?string,
??criteria:?Partial
):?Person[]?{
??return?persons
????.filter((person)?=>?person.type?===?personType)
????.filter((person)?=>?{
??????let?criteriaKeys?=?Object.keys(criteria)?as?(keyof?Person)[];
??????return?criteriaKeys.every((fieldName)?=>?{
????????return?person[fieldName]?===?criteria[fieldName];
??????});
????});
}
export?const?usersOfAge23?=?filterPersons(persons,?"user",?{?age:?23?});
export?const?adminsOfAge23?=?filterPersons(persons,?"admin",?{?age:?23?});
console.log("Users?of?age?23:");
usersOfAge23.forEach(logPerson);
console.log();
console.log("Admins?of?age?23:");
adminsOfAge23.forEach(logPerson);
前置知識
泛型 Partial 泛型 函數(shù)重載
思路
題目描述也懶得看了, 直接看報錯。

報錯信息提示我們沒有找到合適的函數(shù)重載。因此我的思路就是補(bǔ)上合適的重載即可。關(guān)于函數(shù)重載,我的系列教程不涉及,大家可以看下官網(wǎng)資料。
重載之后,不同的情況調(diào)用返回值就可以對應(yīng)不同的類型。本題中就是:
如果 personType 是 admin,就會返回 Admin 數(shù)組。 如果 personType 是 user,就會返回 User 數(shù)組。 如果 personType 是其他 string,就會返回 Person 數(shù)組。
代碼
export?function?filterPersons(persons:?Person[],?personType:?'admin',?criteria:?Partial ):?Admin[]
export?function?filterPersons(persons:?Person[],?personType:?'user',?criteria:?Partial ):?User[]
export?function?filterPersons(persons:?Person[],?personType:?string,?criteria:?Partial ):?Person[]?{
????...
}
第七題
題目描述
Intro:
????Filtering?was?completely?removed?from?the?project.
????It?turned?out?that?this?feature?was?just?not?needed
????for?the?end-user?and?we?spent?a?lot?of?time?just?because
????our?office?manager?told?us?to?do?so.?Next?time?we?should
????instead?listen?to?the?product?management.
????Anyway?we?have?a?new?plan.?CEO's?friend?Nick?told?us
????that?if?we?randomly?swap?user?names?from?time?to?time
????in?the?community,?it?would?be?very?funny?and?the?project
????would?definitely?succeed!
Exercise:
????Implement?swap?which?receives?2?persons?and?returns?them?in
????the?reverse?order.?The?function?itself?is?already
????there,?actually.?We?just?need?to?provide?it?with?proper?types.
????Also?this?function?shouldn't?necessarily?be?limited?to?just
????Person?types,?lets?type?it?so?that?it?works?with?any?two?types
????specified.
題目大概意思是讓你修改 swap 函數(shù),使得不報錯。并且,我希望這個函數(shù)可以適用于任意兩個變量,不管其類型一樣不一樣, 也不管二者類型是什么。
題目內(nèi)置代碼
interface?User?{
??type:?"user";
??name:?string;
??age:?number;
??occupation:?string;
}
interface?Admin?{
??type:?"admin";
??name:?string;
??age:?number;
??role:?string;
}
function?logUser(user:?User)?{
??const?pos?=?users.indexOf(user)?+?1;
??console.log(`?-?#${pos}?User:?${user.name},?${user.age},?${user.occupation}`);
}
function?logAdmin(admin:?Admin)?{
??const?pos?=?admins.indexOf(admin)?+?1;
??console.log(`?-?#${pos}?Admin:?${admin.name},?${admin.age},?${admin.role}`);
}
const?admins:?Admin[]?=?[
??{
????type:?"admin",
????name:?"Will?Bruces",
????age:?30,
????role:?"Overseer",
??},
??{
????type:?"admin",
????name:?"Steve",
????age:?40,
????role:?"Steve",
??},
];
const?users:?User[]?=?[
??{
????type:?"user",
????name:?"Moses",
????age:?70,
????occupation:?"Desert?guide",
??},
??{
????type:?"user",
????name:?"Superman",
????age:?28,
????occupation:?"Ordinary?person",
??},
];
export?function?swap(v1,?v2)?{
??return?[v2,?v1];
}
function?test1()?{
??console.log("test1:");
??const?[secondUser,?firstAdmin]?=?swap(admins[0],?users[1]);
??logUser(secondUser);
??logAdmin(firstAdmin);
}
function?test2()?{
??console.log("test2:");
??const?[secondAdmin,?firstUser]?=?swap(users[0],?admins[1]);
??logAdmin(secondAdmin);
??logUser(firstUser);
}
function?test3()?{
??console.log("test3:");
??const?[secondUser,?firstUser]?=?swap(users[0],?users[1]);
??logUser(secondUser);
??logUser(firstUser);
}
function?test4()?{
??console.log("test4:");
??const?[firstAdmin,?secondAdmin]?=?swap(admins[1],?admins[0]);
??logAdmin(firstAdmin);
??logAdmin(secondAdmin);
}
function?test5()?{
??console.log("test5:");
??const?[stringValue,?numericValue]?=?swap(123,?"Hello?World");
??console.log(`?-?String:?${stringValue}`);
??console.log(`?-?Numeric:?${numericValue}`);
}
[test1,?test2,?test3,?test4,?test5].forEach((test)?=>?test());
前置知識
泛型
思路
題目廢話很多, 直接忽略看報錯。

這個其實(shí)我在 你不知道的 TypeScript 泛型(萬字長文,建議收藏) 里也講過了,直接看代碼。
代碼
export?function?swap<U,?T>(v1:?T,?v2:?U):?[U,?T]?{
??return?[v2,?v1];
}
第八題
題目描述
Intro:
????Project?grew?and?we?ended?up?in?a?situation?with
????some?users?starting?to?have?more?influence.
????Therefore,?we?decided?to?create?a?new?person?type
????called?PowerUser?which?is?supposed?to?combine
????everything?User?and?Admin?have.
Exercise:
????Define?type?PowerUser?which?should?have?all?fields
????from?both?User?and?Admin?(except?for?type),
????and?also?have?type?'powerUser'?without?duplicating
????all?the?fields?in?the?code.
題目大概意思是定義一個類型 PowerUser, 里面包含 User 和 Admin 的所有屬性, 并且有一個字段是固定的 type: 'powerUser'。
題目內(nèi)置代碼
interface?User?{
??type:?"user";
??name:?string;
??age:?number;
??occupation:?string;
}
interface?Admin?{
??type:?"admin";
??name:?string;
??age:?number;
??role:?string;
}
type?PowerUser?=?Omit"type">?&?{?type:?"powerUser"?};
export?type?Person?=?User?|?Admin?|?PowerUser;
export?const?persons:?Person[]?=?[
??{
????type:?"user",
????name:?"Max?Mustermann",
????age:?25,
????occupation:?"Chimney?sweep",
??},
??{?type:?"admin",?name:?"Jane?Doe",?age:?32,?role:?"Administrator"?},
??{?type:?"user",?name:?"Kate?Müller",?age:?23,?occupation:?"Astronaut"?},
??{?type:?"admin",?name:?"Bruce?Willis",?age:?64,?role:?"World?saver"?},
??{
????type:?"powerUser",
????name:?"Nikki?Stone",
????age:?45,
????role:?"Moderator",
????occupation:?"Cat?groomer",
??},
];
function?isAdmin(person:?Person):?person?is?Admin?{
??return?person.type?===?"admin";
}
function?isUser(person:?Person):?person?is?User?{
??return?person.type?===?"user";
}
function?isPowerUser(person:?Person):?person?is?PowerUser?{
??return?person.type?===?"powerUser";
}
export?function?logPerson(person:?Person)?{
??let?additionalInformation:?string?=?"";
??if?(isAdmin(person))?{
????additionalInformation?=?person.role;
??}
??if?(isUser(person))?{
????additionalInformation?=?person.occupation;
??}
??if?(isPowerUser(person))?{
????additionalInformation?=?`${person.role},?${person.occupation}`;
??}
??console.log(`${person.name},?${person.age},?${additionalInformation}`);
}
console.log("Admins:");
persons.filter(isAdmin).forEach(logPerson);
console.log();
console.log("Users:");
persons.filter(isUser).forEach(logPerson);
console.log();
console.log("Power?users:");
persons.filter(isPowerUser).forEach(logPerson);
前置知識
集合操作(交叉類型) & 操作符 泛型 Omit 泛型
思路
從題目信息不難看出,就是讓我們實(shí)現(xiàn) PowerUser。
有前面的分析不難得出我們只需要:
合并 User 和 Admin 的屬性即可。借助 & 操作符可以實(shí)現(xiàn)。即 User & Admin。增加特有的屬性 type: powerUser。首先去掉上一步合并的 type 屬性, 然后繼續(xù)和 { type: "powerUser" } 交叉即可。 增加 { type: "powerUser" } 之前使用內(nèi)置泛型 Omit 將原本的 type 刪掉即可。
代碼
type?PowerUser?=?Omit"type">?&?{?type:?"powerUser"?};
總結(jié)
以上就是給大家?guī)淼念}目解析。這八道題的考點(diǎn)有,按照我個人理解的重要程度劃分為:
type 和 interface 的基本操作(必須掌握) 聯(lián)合類型 和 交叉類型(強(qiáng)烈建議掌握) 類型斷言和類型收縮(強(qiáng)烈建議掌握) 泛型和常見內(nèi)置泛型(強(qiáng)烈建議掌握) 函數(shù)重載(推薦掌握)
最后祝愿大家告別 anyscript,成為 TypeScript 魔法師
Reference
深入理解 TypeScript: https://jkchao.github.io/typescript-book-chinese/
[2]TypeScript 官方文檔: https://www.typescriptlang.org/docs/home
[3]typescript-exercises: https://typescript-exercises.github.io/
推薦閱讀
2、你不知道的 TypeScript 泛型(萬字長文,建議收藏)
3、尤大 3 天前發(fā)在 GitHub 上的 vue-lit 是啥?
如果覺得文章不錯,幫忙點(diǎn)個在看唄
