最近很常用到 TypeScript, GraphQL, gRPC,有時候寫 code 寫了一整天實在很容易混淆,決定來稍微整理一下這三個工具的型別。
Table of Contents
1. TypeScript
1.1 常用型別
// string
const testString: string = 'Hello world';
// number
const testNumber: number = 10;
const testFloat: number = 10.5;
// boolean
const testBoolean: boolean = true;
// null
const testNull: null = null;
// undefined
const testUndefined: undefined = undefined;
// array
const testArray1: number[] = [1, 2, 3];
const testArray2: string[] = ['apple', 'banana', 'orange'];
const testArray3: Array<number> = [1, 2, 3];
const testArray4: Array<string> = ['apple', 'banana', 'orange'];
// object
// ? 代表非必要 property ; 屬性之間用 ',' 或 ';' 隔開都可以
const testObj1: {name: string, age: number} = {name: 'Jimmy', age: 18};
const testObj2: {name: string; age?: number} = {name: 'Jimmy'};
// any
const testAny: any = ['apple', 5, {name: 'Jimmy'}]
// function
// 在變數的 () 後面指定要 return 的 type
const testFunction = (num: number): string => {
return `${num} apple`
}
testFunction(5);
// union types
let testUnion: string | number = 5;
testUnion = 'Jimmy';
testUnion = 5;
1.2 Type Aliases ( 型別化名 )
// type aliases
type ID = string | number;
const id1: ID = '1';
const id2: ID = 2;
type TypeArr = number[];
const typeArr: TypeArr = [1, 2, 3];
type TestObject1 = {
name: string,
age: number,
isAdult?: boolean
}
const testObject: TestObject1 = {
name: 'Jimmy',
age: 18,
isAdult: true
}
const testArray: TestObject1[] = [
{
name: 'Jimmy',
age: 18,
isAdult: true
},
{
name: 'Sandy',
age: 18
}
]
type TestObject2 = TestObject1 & {
address: string
}
const testObject2: TestObject2 = {
name: 'Jimmy',
age: 18,
isAdult: true,
address: 'Taipei'
}
1.3 Interfaces
// interfaces
interface IDInterface = string | number;
interface ArrInterface = number[];
// error! interfaces 只能指定 object 的型別
interface TestInterface1 {
name: string
}
interface TestInterface2 extends TestInterface1 {
age: number
}
const testInterface1: TestInterface1 = {
name: 'Jimmy'
}
const testInterface2: TestInterface2 = {
name: 'Jimmy',
age: 18
}
interface DuplicateInterface {
name: string
}
interface DuplicateInterface {
age: number
}
const duplicateInterface1: DuplicateInterface = {
name: 'Jimmy'
}
// error ! DuplicateInterface 現在的型別是:{name: string, age: number}
const duplicateInterface2: DuplicateInterface = {
name: 'Jimmy',
age: 18
}
1.4 Type Aliases 和 Interfaces 的差別
- Interfaces 只能指定 object 的型別,type aliases 可以指定任意型別
- Interfaces 可以用 extends 繼承其他 interfaces ,type aliases 用 ‘&’ (交集) 來達到類似的功能
- 重複宣告一樣的 interface 時,interface 會自動繼承原本同名的型別,type aliases 重複宣告一樣名字時則會 throw error
1.5 Enums ( 列舉、枚舉 )
// numeric enums
enum Direction1 {
Up = 1,
Down,
Left,
Right,
}
console.log(Direction1);
/*
{
'1': 'Up',
'2': 'Down',
'3': 'Left',
'4': 'Right',
Up: 1,
Down: 2,
Left: 3,
Right: 4
}
*/
enum Direction2 {
Up,
Down,
Left,
Right,
}
console.log(Direction2);
/*
{
'0': 'Up',
'1': 'Down',
'2': 'Left',
'3': 'Right',
Up: 0,
Down: 1,
Left: 2,
Right: 3
}
*/
// string enums
enum Direction3 {
Up = 'UP_string',
Down = 'Down_string',
Left = 'Left_string',
Right = 'Right_string',
}
console.log(Direction3)
/*
{
Up: 'UP_string',
Down: 'Down_string',
Left: 'Left_string',
Right: 'Right_string'
}
*/
// iteration of numeric enum
for(const value in Direction1) {
console.log(value);
}
/*
1
2
3
4
Up
Down
Left
Right
*/
for(const value in Direction1) {
console.log(Direction1[value]);
}
/*
Up
Down
Left
Right
1
2
3
4
*/
for(const value in Direction1) {
if (isNaN(Number(value))) {
console.log(value);
}
}
/*
Up
Down
Left
Right
*/
// iteration of string enum
for(const value in Direction3) {
console.log(value);
}
/*
Up
Down
Left
Right
*/
for(const value of Object.values(Direction3)) {
console.log(value);
}
/*
UP_string
Down_string
Left_string
Right_string
*/
const keys: (keyof typeof Direction3)[] = <(keyof typeof Direction3)[]>Object.keys(Direction3);
console.log(keys);
// [ 'Up', 'Down', 'Left', 'Right' ]
for (const key of keys) {
const direction3_string: string = Direction3[key];
console.log(direction3_string);
}
/*
UP_string
Down_string
Left_string
Right_string
*/
1.6 Type Assertion ( 型別斷言 )
function printId(id: number | string) {
if (id.length) {
console.log(id.length)
} else {
console.log(id.toString().length)
}
}
// error!! Property 'length' does not exist on type 'string | number'.
// Property 'length' does not exist on type 'number'.
function printId(id: number | string) {
if ((<string>id).length) {
console.log((<string>id).length)
} else {
console.log(id.toString().length)
}
}
printId('Jimmy')
// 5
printId(10);
// 2
let file = {}
file.name = 'Jimmy';
// error: Property 'name' does not exist on type '{}'.
file.age = 18
// error: Property 'name' does not exist on type '{}'
type FileType = {
name: string,
age: number
}
let file = <FileType> {}
// or let file = {} as FileType
file.name = 'Jimmy';
file.age = 18
2. GraphQL
type Person {
id: ID!
# 宣告為 ID 的欄位可以輸入 String 或是 Int,但最後都會被轉為 String
# ! 代表此欄位必填
name: String
age: Int
isAdult: Boolean
weight: Float
childrenName: [String]
}
# input 代表要在 request 帶的變數的型別
input PersonConfig {
id: ID!
}
3. gRPC
// gRPC.proto
message UserConfigRequest {
int32 id = 1;
}
message CarInfo {
string brand = 1;
int32 horsepower = 2;
bool isTurbo = 3;
}
message UserResponse {
string name = 1;
int32 age = 2;
sint32 property = 3;
bool isAdult = 4;
float weigth = 5;
repeated CarInfo cars = 6;
repeated string childrenName = 7;
}
/* example for UserResponse
{
name: 'Jimmy',
age: 18,
property: -10000,
isAdult: true,
weight: 75.5,
cars: [
{ brand: 'Ford', horsepower: 180 },
{ brand: 'Mazada', horsepower: 150, isTurbo: false}
],
children: ['Jenny', 'Tommy']
}
*/
service UserService {
rpc UserData(UserConfigRequest) returns (UserResponse);
}
4. 參考資料
TypeScript:
TypeScript: Documentation – Everyday Types
Iterating a TypeScript Enum – Peter Morlion
TypeScript | 善用 Enum 提高程式的可讀性 – 基本用法 feat. JavaScript
Type Assertion in TypeScript
GraphQL:
Schemas and Types | GraphQL
學習 GraphQL 筆記(一)Object Type