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
}>
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 & Btype 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 | Btype 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)
}
}