TypeScript考試題來了,60%的人不會(附答案)

来源:https://www.cnblogs.com/chengxuyuanaa/archive/2020/06/08/13068661.html
-Advertisement-
Play Games

前言 1.相信這段時間來,對 TypeScript 感興趣的小伙伴們已經把這個神器給系統的學習了一遍了吧。如果計劃開始學習但是還沒有開始,或者沒有找到資料的同學,可以看下我在之前文章中 前端進階指南 找一下 TypeScript 部分的教程,自行學習。 本文從最近在 Github 上比較火的倉庫 t ...


前言

1.相信這段時間來,對 TypeScript 感興趣的小伙伴們已經把這個神器給系統的學習了一遍了吧。如果計劃開始學習但是還沒有開始,或者沒有找到資料的同學,可以看下我在之前文章中 前端進階指南 找一下 TypeScript 部分的教程,自行學習。

本文從最近在 Github 上比較火的倉庫 typescript-exercises 入手,它的中文介紹是 「富有挑戰性的 TypeScript 練習集」。裡面包含了 15 個 TypeScript 的練習題,我會從其中挑選出幾個比較有價值的題目,一起來解答一下。
2.光理論是不夠的。在此贈送2020最新企業級 Vue3.0/Js/ES6/TS/React/node等實戰視頻教程,想學的可進裙 519293536 免費獲取,小白勿進哦!

目標

來看下這個倉庫的發起者所定下的目標,讓每個人都學會以下知識點的實戰運用:

  1. Basic typing.
  2. Refining types.
  3. Union types.
  4. Merged types.
  5. Generics.
  6. Type declarations.
  7. Module augmentation.
  8. Advanced type mapping.

真的都是一些非常有難度且實用的知識點,掌握了它們一定會讓我們在編寫 TypeScript 類型的時候如虎添翼。

挑戰

exercise-00

題目

import chalk from "chalk"

// 這裡需要補全
const users: unknown[] = [
  {
    name: "Max Mustermann",
    age: 25,
    occupation: "Chimney sweep",
  },
  {
    name: "Kate Müller",
    age: 23,
    occupation: "Astronaut",
  },
]

// 這裡需要補全
function logPerson(user: unknown) {
  console.log(` - ${chalk.green(user.name)}, ${user.age}`)
}

console.log(chalk.yellow("Users:"))
users.forEach(logPerson)
複製代碼

解答

第一題只是個熱身題,考察對介面類型定義的掌握,直接定義 User 介面即可實現。

interface User {
  name: string
  age: number
  occupation: string
}

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

function logPerson(user: User) {
  console.log(` - ${chalk.green(user.name)}, ${user.age}`)
}

console.log(chalk.yellow("Users:"))
users.forEach(logPerson)
複製代碼

或者利用類型推導,users 數組會自動推斷出類型:

const users = [
  {
    name: "Max Mustermann",
    age: 25,
    occupation: "Chimney sweep",
  },
  {
    name: "Kate Müller",
    age: 23,
    occupation: "Astronaut",
  },
]
複製代碼

在 VSCode 中,滑鼠放到 users 變數上即可看到類型被自動推斷出來了:

{
  name: string
  age: number
  occupation: string
}
;[]
複製代碼

那麼利用 typeof 關鍵字,配合索引查詢,我們也可以輕鬆取得這個類型。這裡 number 的意思就是查找出 users 的所有數字下標對應的值的類型集合。

type User = typeof users[number]
複製代碼

這個倉庫提供了每道題的答題機制,執行 npm run 0 對應題號,看到結果即可證明編譯通過,答案正確。

 

 

execsise-01

題目

最初,我們在資料庫中只有 User 類型,後來引入了 Admin 類型。把這兩個類型組合成 Person 類型以修複錯誤。

interface User {
  name: string
  age: number
  occupation: string
}

interface Admin {
  name: string
  age: number
  role: string
}

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",
  },
]

function logPerson(user: User) {
  console.log(` - ${chalk.green(user.name)}, ${user.age}`)
}

persons.forEach(logPerson)
複製代碼

解答

本題考查聯合類型的使用:

// 定義聯合類型
type Person = User | Admin

const persons: Person[] /* <- 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",
  },
]

function logPerson(user: Person) {
  console.log(` - ${chalk.green(user.name)}, ${user.age}`)
}
複製代碼

exercise-02

根據上題中定義出的 Person 類型,現在需要一個方法列印出它的實例:

題目

function logPerson(person: Person) {
  let additionalInformation: string
  if (person.role) {
    // ❌ 報錯 Person 類型中不一定有 role 屬性
    additionalInformation = person.role
  } else {
    // ❌ 報錯 Person 類型中不一定有 occupation 屬性
    additionalInformation = person.occupation
  }
  console.log(
    ` - ${chalk.green(person.name)}, ${person.age}, ${additionalInformation}`,
  )
}
複製代碼

解答

本題考查 TypeScript 中的「類型保護」,TypeScript 的程式流分析使得某些判斷代碼包裹之下的代碼中,類型可以被進一步收縮。

in 操作符:

function logPerson(person: Person) {
  let additionalInformation: string
  if ("role" in person) {
    additionalInformation = person.role
  } else {
    additionalInformation = person.occupation
  }
  console.log(
    ` - ${chalk.green(person.name)}, ${person.age}, ${additionalInformation}`,
  )
}
複製代碼

函數返回值中的 is 推斷:

function isAdmin(user: Person): user is Admin {
  return user.hasOwnProperty("role")
}

function logPerson(person: Person) {
  let additionalInformation: string
  if (isAdmin(person)) {
    additionalInformation = person.role
  } else {
    additionalInformation = person.occupation
  }
  console.log(
    ` - ${chalk.green(person.name)}, ${person.age}, ${additionalInformation}`,
  )
}
複製代碼

exercise-04

題目

本題定義了一個 filterUsers 方法,用來通過 person 中的某些欄位來篩選出用戶的子集。

function filterUsers(persons: Person[], criteria: User): User[] {
  return persons.filter(isUser).filter((user) => {
    let criteriaKeys = Object.keys(criteria) as (keyof User)[]
    return criteriaKeys.every((fieldName) => {
      return user[fieldName] === criteria[fieldName]
    })
  })
}

console.log(chalk.yellow("Users of age 23:"))

filterUsers(
  persons,
  // ❌ 報錯,criteria 定義的是精確的 User 類型,少欄位了。
  {
    age: 23,
  },
).forEach(logPerson)
複製代碼

可以看出,由於 filterUsers 的第二個篩選參數的類型被精確的定義為 User,所以只傳入它的一部分欄位 age 就會報錯。

解答

本題考查 [mapped-types](https://www.typescriptlang.org/docs/handbook/advanced-types.html#mapped-types) 映射類型,

type Criteria = {
  [K in keyof User]?: User[K]
}

function filterUsers(persons: Person[], criteria: Criteria): User[] {
  return persons.filter(isUser).filter((user) => {
    let criteriaKeys = Object.keys(criteria) as (keyof User)[]
    return criteriaKeys.every((fieldName) => {
      return user[fieldName] === criteria[fieldName]
    })
  })
}
複製代碼

Criteria 利用了映射類型,把 User 的 key 值遍歷了一遍,並且加上了 ? 標誌代表欄位都是可選的,即可完成任務。

也可以利用內置類型 Partial,這個類型用於把另一個類型的欄位全部轉為可選。

function filterUsers(persons: Person[], criteria: Partial<User>): User[] {}
複製代碼

exercise-05

題目

function filterPersons(
  persons: Person[],
  personType: string,
  criteria: unknown,
): unknown[] {}

let usersOfAge23: User[] = filterPersons(persons, "user", { age: 23 })
let adminsOfAge23: Admin[] = filterPersons(persons, "admin", { age: 23 })
複製代碼

解答

本題返回值類型即可以是 User,也可以是Admin,並且很明顯這個結果是由第二個參數是 'user' 還是 'admin' 所決定。

利用函數重載的功能,可以輕鬆實現,針對每種不同的 personType 參數類型,我們給出不同的返回值類型:

function filterPersons(
  persons: Person[],
  personType: "admin",
  criteria: Partial<Person>,
): Admin[]
function filterPersons(
  persons: Person[],
  personType: "user",
  criteria: Partial<Person>,
): User[]
function filterPersons(
  persons: Person[],
  personType: string,
  criteria: Partial<Person>,
) {}

let usersOfAge23: User[] = filterPersons(persons, "user", { age: 23 })
let adminsOfAge23: Admin[] = filterPersons(persons, "admin", { age: 23 })
複製代碼

exercise-06

題目

function swap(v1, v2) {
  return [v2, v1]
}

function test1() {
  console.log(chalk.yellow("test1:"))
  const [secondUser, firstAdmin] = swap(admins[0], users[1])
  logUser(secondUser)
  logAdmin(firstAdmin)
}

function test2() {
  console.log(chalk.yellow("test2:"))
  const [secondAdmin, firstUser] = swap(users[0], admins[1])
  logAdmin(secondAdmin)
  logUser(firstUser)
}
複製代碼

解答

本題的關鍵點是 swap 這個方法,它即可以接受Admin類型為參數,也可以接受 User 類型為參數,並且還需要根據傳入參數的順序把它們倒過來放在數組中放回。

也就是說傳入的是 v1: User, v2: Admin,需要返回 [Admin, User] 類型。

這題就比較有難度了,首先需要用到泛型 來推斷出參數類型,並且和結果關聯起來:

function swap<T, K>(v1: T, v2: K) {
  return [v2, v1]
}
複製代碼

此時結果沒有按照我們預期的被推斷成 [K, T],而是被推斷成了 (K | T)[],這是不符合要求的。

這是因為 TypeScript 預設我們數組中的元素是可變的,所以它會「悲觀的」推斷我們可能會改變元素的順序。滑鼠放到運行函數時的swap上,我們可以看出類型被推斷為了:

function swap<Admin, User>(v1: Admin, v2: User): (Admin | User)[]
複製代碼

要改變這一行為,我們加上 as const 來聲明它為常量,嚴格保證順序。

function swap<T, K>(v1: T, v2: K) {
  return [v2, v1] as const
}
複製代碼

此時再看看 swap的推斷:

function swap<Admin, User>(v1: Admin, v2: User): readonly [User, Admin]
複製代碼

類型被自動加上了 readonly,並且也嚴格的維持了順序,太棒啦。

總結

1.先用其中的幾道題練練手,到第六題的時候,已經涉及到了一些進階的用法,非常有挑戰性。不知道小伙伴們對於這樣的做題形式是否感興趣,還剩下不少有挑戰性的題目,如果反饋不錯的話,我會繼續更新這個系列。
2.光理論是不夠的。在此贈送2020最新企業級 Vue3.0/Js/ES6/TS/React/node等實戰視頻教程,想學的可進裙 519293536 免費獲取,小白勿進哦!

本文的文字及圖片來源於網路加上自己的想法,僅供學習、交流使用,不具有任何商業用途,版權歸原作者所有,如有問題請及時聯繫我們以作處理


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、活動里的方法 (1)onCreate方法在活動創建之後必定執行的方法。 (2)Andorid程式講究​邏輯和視圖相分離; (3)setContentView()靜態方法,用於引入​視圖; (4)Android程式不推薦在程式中對字元串進行硬編碼​,最好的做法就是一般把字元串定義到res/valu ...
  • ##BindView ButterKnife 優勢 綁定組件方便,使用簡單 處理點擊事件方便,如adapter中的viewholder 同時父組件綁定後子組件無需綁定 註意 在setcontentview之後使用,且子空間不可再使用static final屬性 ##在不改變按鈕圖片大小的情況,擴大點 ...
  • 新聞 Android 11特性調整:安裝外部來源應用需要重啟APP Google Messages beta版迎來一個新的搜索框 Android開發者生態永遠比不上iOS?“聯盟與公約”們正改變這一點 谷歌新款Android TV串流設備外形曝光:代號Sabrina Android 11 Beta引 ...
  • 前言 大白(Baymax),迪士尼動畫《超能陸戰隊》中的健康機器人,是一個體型胖胖的充氣機器人,因呆萌的外表和善良的本質獲得大家的喜愛,被稱為“萌神”。 Baymax項目是為了減少開發人員在開發中一些不規範的代碼編寫造成的記憶體泄露,界面卡頓,耗電等問題而來的一個監控系統。 現在Baymax迎來了它新 ...
  • Vuex源碼閱讀(一) ,介紹Vuex的執行順序,以及new Vuex.Store()的時候內部都幹了什麼。 ...
  • 效果展示 頁面截圖 相關效果 預覽 html 頁面 從微信讀書上找了幾張書籍封面來做輪播的圖片。 index.html <body> <div id="container"> <div class="big_pic_div"> <div class="prev"></div> <div class= ...
  • 前言 本文所分享的是關於 vue 3.x 在用法上的改變,而不是在代碼實現上的不同。 雖然 vue2 到 vue3 的實現大改,但在用法上變化基本不大,比較明顯的一個變化就是添加了 setup(){} 函數了,幾乎所有的配置變成了以函數的方式進行定義。即使是這樣,但小改動還是很多的。本文主要分享的是 ...
  • 前言:都2020年了,感覺是時候該學一波webpack了,趁著最近有時間,就學了一下,等把官網上的webpack結構和模塊大概看了一遍之後,就感覺可以開始搭個項目實戰一下了,從0開始,一步步記錄下來 使用版本: webpack4.x 1.包含插件和loader * html: html-webpac ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...