Skip to main content

[TS] TypeScript Basic Type

此篇為各筆記之整理,非原創內容,資料來源可見下方連結與文後參考資料。

  • Top Types:指的是可以接受所有值的型別,包含 anyunknown
  • Bottom Types:指的是不能接受任何值的型別,即 never

Array and Tuple#

keywords: ReadonlyArray#
let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3]; // 等同於
let list: ReadonlyArray<number> = [1, 2, 3]; // 不能修改 Array 裡的元素

Tuple 則是有固定長度和元素型別的陣列:

let tuple: [number, string, boolean, number] = [3, 'foo', true, 10];
tuple = [4, 'bar', false, 30];

Tuples with Label(帶有標籤的 tuple):

  • 滑鼠移到該 type 上時,會顯示每個元素的 label
type PersonInfo = [name: string, country: string, age: number];
const aaron: PersonInfo = ['Aaron', 'Taiwan', 32];

Enum#

enum Color {
Red,
Green,
Blue,
}
const c: Color = Color.Red; // 0
enum SelectableButtonTypes {
Important = 'important',
Optional = 'optional',
Irrelevant = 'irrelevant',
}

Any#

一開始宣告變數時,如果對變數不設值,且為註記型別,都會被視為是 any:

let foo; // any

Unknown#

unknownany 一樣,該型別的變數都可以帶入任意的值(屬於 Top Types),但有一個關鍵的差異在於,開發者不能對 unknown 型別的變數進行任何操作,但 any 可以:

let isAny: any;
let isUnknown: unknown;
isAny.hello(); // 可以對 any 型別進行任何操作
isUnknown.hello(); // 不可以對 unknown 型別進行任何操作

Unknown 適合用在

  1. 當該變數不希望再被操作的情況
  2. 該變數型別未知,需要在被限縮(narrow)後才能使用

因此,對於 unknown 型別,通常會搭配 narrow 或 type guard 使用。

真的必要的話才使用 Type Assertions 的方式:

let isUnknown: unknown = {
foo: 'foo',
hello: () => console.log('hello'),
};
// ❌ 錯誤:不可以對 unknown 型別進行任何操作
isUnknown.foo;
isUnknown.hello();
type Hello = {
foo: string;
hello: () => void;
};
// ⭕️ 正確:對 unknown 型別需要使用 Type Assertions
(<Hello>isUnknown).foo;
(isUnknown as Hello).hello();

Nullable Types#

包含 nullundefined

// Not much else we can assign to these variables!
let u: undefined = undefined;
let n: null = null;

Object#

任何除了原始型別(primitive type)之外的,都可以算是 Object Type。原始型別包含:數值、字串、布林、symbolnullundefined

使用 Object Literal 建立物件#

當我們使用 Object literal 建立物件而沒有宣告型別時,實際上 TS 是建立一個 Type,而不是套用 object 型別:

let foo = {
s: 'string',
n: 3,
b: true,
};

實際上這個 foo 套用了一個 Type:

type Foo = {
s: string;
n: number;
b: boolean;
};

因此將不能對 foo 的這物件去新增屬性,例如,foo.bar = 'bar' 是不被允許的。

物件的解構賦值#

const aaronChen = { firstName: 'Aaron', age: 31 };
const { firstName, age }: { firstName: string; age: number } = aaronChen;

階層更深的解構賦值:

const aaronChen = {
location: {
zipCode: 110,
city: 'Taipei',
},
};
const {
location: { zipCode, city },
}: { location: { zipCode: number; city: string } } = aaronChen;

在函式參數使用物件的解構賦值#

// Use arrow function
const getName = (person: { firstName: string; lastName: string }) => {
const { firstName, lastName } = person;
return `${firstName} ${lastName}`;
};
// Or normal named function
function getName(person: { firstName: string; lastName: string }) {
const { firstName, lastName } = person;
return `${firstName} ${lastName}`;
}
const name = getName({
firstName: 'Aaron',
lastName: 'Chen',
});

never#

  • never 使用的時間點是該函式會「卡在裡出不來」例如,無窮迴圈;或者「終止在函式裡」,例如,拋出錯誤。
  • never 是任何型別的 prototype,因此不用再額外使用 number | never 這種寫法。
  • Never 是用來提醒開發者,這個函式執行後,程式將無法繼續往後執行。
function showNever(arg: string | number) {
if (typeof arg === 'string') {
arg.split(',');
} else if (typeof arg === 'number') {
arg.toFixed(2);
} else {
// here, arg is never
}
}

Type Assertions#

有些時候你會比 TypeScript 對於資料的型別更清楚,這時候透過 Type Assertions,你可以告訴 TypeScript 的編譯器說「請相信我,以我的為準,我知道我自己在做什麼」。

Type Assertions 有兩種主要的用法,一種是使用角括弧(<>),一種是用 as

const data = JSON.stringify({
foo: 'foo',
bar: 'bar',
});
// 使用 <> 進行 type assertions
let parsedJSON1 = <{ foo: string; bar: string }>JSON.parse(data);
// 使用 as 進行 type assertions
let parsedJSON2 = JSON.parse(data) as { foo: string; bar: string };

也可以搭配 Type Alias 使用:

type Data = {
foo: string;
bar: string;
};
// 使用 <> 進行 type assertions
let parsedJSON1 = <Data>JSON.parse(data);
// 使用 as 進行 type assertions
let parsedJSON2 = JSON.parse(data) as Data;

和 type alias 一樣,type assertions 會在 compile 階段被移除,並且不會影響到 runtime 時的行為。

參考#

Last updated on