跳到主要内容

Type(@chibicode)

轉載自: TypeScript Tutorial for JS Programmers Who Know How to Build a Todo App

Types就像小型的單元測試

Types are like lightweight, automatic unit tests

Using TypeScript to Check Data

type Todo = {
id: number
text: string
done: boolean
}

toggleTodo 的 input type 必須為 Todo

// Parameter "todo" must match the Todo type
function toggleTodo(todo: Todo) {
// ...
}

toggleTodo 的 input type 必須為 Todo ,且 return 的 type 也為 Todo

// The return value must match the Todo type
function toggleTodo(todo: Todo): Todo {
// Missing the "id" property
return {
text: todo.text,
done: !todo.done
}
}

readonly properties

為了防止原值被更改,必須回傳新值,如下

// toggleTodo must return a new todo object.
function toggleTodo(todo: Todo): Todo {
// Little Duckling’s refactoring is a
// bad refactoring because it modifies
// the argument (input) todo object
todo.done = !todo.done
return todo
}

可設定 type 為 readonly

type Todo = {
readonly id: number
readonly text: string
readonly done: boolean
}
function toggleTodo(todo: Todo): Todo {
// Little Duckling’s refactoring
todo.done = !todo.done
return todo
}
// 編譯結果
// Cannot assign to 'done' because it is a read-only property

正確結果

type Todo = {
readonly id: number
readonly text: string
readonly done: boolean
}
// Earlier implementation: it will continue to
// work because the input todo is not modified
function toggleTodo(todo: Todo): Todo {
return {
id: todo.id,
text: todo.text,
done: !todo.done
}
}

Mapped Types映射類型

type Todo = {
readonly id: number
readonly text: string
readonly done: boolean
}

其等價於

// Readonly<...> makes each property readonly
type Todo = Readonly<{
id: number
text: string
done: boolean
}>

&lt;img src=&quot;readonly.svg&quot;/&gt;

Required<...>, Partial<...>, etc 都算是 mapped types。 用起來像 functions,但其 input/output 是 TypeScript types.

// Property Modifiers屬性修飾符: ?, readonly
interface Person {
firstName: string;
lastName?: string; // lastName is optional
readonly age: number; // you should not mutate the age
}

請把在 Mapped Type 中修改物件的 property modifiers:理解 Partial、Required 和 Readonly 的實作 看完

大意:

  • Required<...> : -? 把可選全移除
  • Partial<...> : +?(+為預設值可省略) 把全部設成可選

Array Types

假如要完成 “Mark all as completed“ 功能,元件的資料會如下

[
{ id: 1, text: 'First todo', done: true },
{ id: 2, text: 'Second todo', done: true }
]

將會設計 completeAll() 去得到資料

Readonly Todo type

type Todo = Readonly<{
id: number
text: string
done: boolean
}>

completeAll()的輸入會如下,[] as array type

// Takes an array of todo items and returns
// a new array where "done" is all true
function completeAll(todos: Todo[]) {
// ...
}

completeAll()的回傳也要是 array

// Output is an array of Todo items: Todo[]
function completeAll(todos: Todo[]): Todo[] {
// ...
}

completeAll()的回傳必須是新值

// Output is an array of Todo items: Todo[]
function completeAll(todos: readonly Todo[]): Todo[] {
// ...
}

Intersection Types (交集型別)

一個 And 的概念

A & B
type A = { a: number }
type B = { b: string }
// This intersection type…
type AandB = A & B
// …is equivalent to:
type AandB = {
a: number
b: string
}

如果第二種類型比第一種類型更具體(specific),則第二種類型會覆蓋第一種類型。這是一個例子:

// They booth have a property foo,
// but B’s foo (true) is
// more specific than A’s foo (boolean)
type A = { foo: boolean }
type B = { foo: true }
// This intersection type…
type AandB = A & B
// …is equivalent to:
type AandB = { foo: true }

結合最上面的例子

type Todo = Readonly<{
id: number
text: string
done: boolean
}>

// type CompletedTodo = Readonly<{
// id: number
// text: string
// done: true //固定值
// }>

// Override the done property of Todo
type CompletedTodo = Todo & {
readonly done: true
}

//最後的completeAll()
function completeAll(todos: readonly Todo[]): CompletedTodo[] {
return todos.map(todo => ({
...todo,
done: true
}))
}

Union Types(聯合型別)

一個 Or 的概念

A | B
type Place = 'home' | 'work' | { custom: string }
// They all compile
const place1: Place = 'home'
const place2: Place = 'work'
const place3: Place = { custom: 'Gym' }
const place4: Place = { custom: 'Supermarket' }

在條件判斷時很好用 Type Guard

// If we have a variable that’s a union type…
type Place = 'home' | 'work' | { custom: string }
function placeToString(place: Place): string {
// TypeScript is smart about what the variable’s
// possible values are for each branch of if/else
if (place === 'home') {
// TypeScript knows place = 'home' here
// (So it won’t compile if you do place.custom)
} else if (place === 'work') {
// TypeScript knows place = 'work' here
// (So it won’t compile if you do place.custom)
} else {
// TypeScript knows place = { custom: … } here
// (So you can do place.custom)
}
}

參考資料