###### tags: `前端`
TyepScript
===
Ts 是js的一種泛型
我們可以透過下面指令來安裝ts
有時候要注意權限問題,可以前面加上sudo
```
npm install -g typescript
```
在創建ts檔案的時候,我們常常都要使用tsc來做轉換
去把我們的.ts檔案轉成.js檔案
```
tsc <file.name>
```
但後來可以使用tsc --init裡面去設定input跟output的資料夾,這樣可以設定好這些東西
主要針對第29行跟 58來做建立設定input output資料夾位置
```
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "commonjs", /* Specify what module code is generated. */
"rootDir": "./src", /* Specify the root folder within your source files. */
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
"outDir": "./dist", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}
```
記得要建立資料夾,名稱可以自己改變
但這樣又太懶惰了
所以我們可以設定--watch在我們每次變更.ts檔案的時候他自動偵測自動轉成js,但記得要丟在你設定的資料夾中歐
```
tsc --watch
```
然後因為這樣也很亂,我們也可以透過node.js去抓我們的js檔案來自動執行,node的18.11版本以上才有這樣的功能.
```
node --watch dist/index.js
```
## Array
我們也可以在不同的datatype做成array
下面分別是一個string跟number的array,若是混用會出現error
```
let names: string[]= ['mario', 'peach', 'luigi', '56']
let ages: number[]= [25, 18, 16, 40]
```
但若是下面這樣混用,datatype也會混亂
這樣f的datatype就會顯示成number, string , boolean 因為他不知道這個array會怎樣變化,雖然現在是暫態但不確定不改變
```
let things =[1,'mango', true]
let f =things[0]
```
若是object可以這樣寫
```
let user: {firstName: string, age: number, id: number} ={
firstName: 'mario',
age: 30,
id: 1
}
## 修改資料但要符合datatype
user.id=5
user.age=35
```
若要修改就是照著datatype來做即可
下面寫法也可
```
let person={
name: 'luigi',
scoer: 35
}
```
## Tuple
早期tuple使用上並沒有辦法用名稱,這導致你只看到datatype不知道你該填入那些位置。
```
let person: [string, number, boolean] = ['mario', 30, false]
```
```
function useCoords():[number, number] {
const lat = 100
const long = 50
return [lat, long]
}
const [lat, long] = useCoords()
```
但近期可以使用了,也更容易讓你知道不同格式分別是哪個variable要被定義
現在可以用下列的方式表述
```
let user: [ name: string, age: number]
user = ['peach', 25]
console.log(user[0])
```
## INTERFACE
interface可以定義一個對象有多少屬性,當然interface裡面也可以內嵌interface
```
interface Author{
name: string,
avatar: string
}
const authorOne: Author ={name:'mario', avatar: '/img/mario.png'}
```
內嵌interface
```
interface Post {
title: string
body: string
tags: string[]
create_at: Date
author: Author
}
const newPost: Post ={
title: 'my post',
body: 'somethign good',
tags: ['gaming', 'tech'],
create_at: new Date(),
author:authorOne
}
```
function也可以這樣寫入
```
function createPost(post: Post): void{
console.log(`Created post ${post.title} by ${post.author.name}`)
}
createPost(newPost)
```
interface也可以寫成ARRAY
```
let posts: Post[] = []
posts.push(newPost)
```
## Custom Type
除了INTERFACE,我們也可以使用自定義的TYPE
像是自訂tuple(但要注意interface不用用等於,直接括號括起來就好)
```
type Rgb = [number, number, number]
function color(x: number): Rgb{
const r = x*255
const g = x*255
const b = x*255
return [r, g, b]
}
console.log(color(5))
```
或是可以自訂object
要記得這種自定義的方法都要給等號
```
type User ={
name: string
score: number
}
const userOne: User = {name: 'mario', score: 52}
function formatuser(user: User): void{
console.log(`${user.name}`)
}
formatuser(userOne)
```
## Union
讓同一種variable可以擁有一種以上的datatype
```
let someId: number | string
someId=5
someId='he'
```
若是你要收email也可以這樣寫
```
let emailAddress: string|null
emailAddress = 'hello@gm.tw'
emailAddress = null
```
但有時候你不太確定你放進去的datatype是哪種,你就可以用typeguard的方式來寫
下面的寫法就可以知道裡面你放進去的DATATYPE來做不同的回覆
```
type Id= number | string
function swapIdType(id: Id) {
if (typeof id === 'string') {
return parseInt(id)
} else {
id.toString()
}
}
const idOne = swapIdType(55)
const idTwo = swapIdType('2')
console.log(idOne, idTwo)
```
Type Guard也可以用來辨識兩種不同的interface
```
interface User{
type: 'user'
username: string
email: string
id: Id
}
interface User{
type: 'person'
firstname: string
age: number
id: Id
}
function logDetails(value: User | Person): void {
if (value.type === 'user') {
console.log(value.username, value.email)
} if (value.type === 'person') {
console.log(value.firstname, value.age)
}
}
```
## Interface Type
interface可以重複被使用,我們看下面的example
hasquantity定義了一個quantity 是number型態
但下面的fruit vehicle or person都有除了quantity以外的attribute,但仍可以被視作是hasQuantity的實作。
接下去看printQuantity若直接把quantity放入也可以直接輸出
但若是直接輸入兩個不同的key value就會報錯,跟上面給的vehicle他們狀況不同,直接給就不能這樣寫,這要注意
```
interface hasQuantity {
quantity: number
}
const something: hasQuantity = { quantity: 50 }
function printQuantity(item: hasQuantity): void {
console.log(`the quantity of the itme ${item.quantity}`)
}
const fruit = {
name: 'mango',
quantity: 50,
}
const vehicle = {
name: 'car',
quantity: 3,
}
const person ={
name: 'mario',
age: 5,
quantity: 50
}
printQuantity(fruit)
printQuantity(vehicle)
printQuantity({quantity: 50})
printQuantity({quantity:50, apple: 80}) # not allow
```
## Function Signature
function signature可以讓你重複使用驗證這些type,以下幾個範例是可以走的
joinTwoNumbers儘管輸入相同,但出來的格式是string,所以不行會報錯
squareNumber儘管只有一個argument,但出來的數字也是number,所以會通過。
基本上你的datatype相同,但少於本身規定的argument數量沒關係,不要超過即可,就不會報錯。
```
type Calculator = (numOne: number, numTwo: number) => number
function addTwoNumber(a:number, b: number){
return a + b
}
function multiplyTwoNumbers(first: number, Second: number){
return first * Second
}
function squareNumber(num: Number){
return num * num
}
function joinTwoNumbers(numOne: number, numTwo: number){
return `${numOne}${numTwo}`
}
let calcs: Calculator[] = []
calcs.push(addTwoNumber)
calcs.push(multiplyTwoNumbers)
calcs.push(joinTwoNumbers) # not allow
calcs.push(squareNumber)
```
當然,把function放到interface中也是可以的
只要最終的數字相同,也可以這樣把他抓出來計算
```
interface HasArea {
name: string
calcArea: (a: number) => number
}
const shapeOne: HasArea = {
name: 'square',
calcArea(l: number) {
return l * l
}
}
const shapeTwo: HasArea = {
name: 'circle',
calcArea(r: number) {
return Math.PI * r^2
}
}
shapeOne.calcArea(5)
shapeTwo.calcArea(3)
```
當然,interface也可以改寫法
```
interface HasArea {
name: string
calcArea(a: number) :number
}
```
## Extending Interface
比較像繼承的狀況先寫一個HasFormatter然後Bill繼承他
```
interface HasFormatter {
format(): string
}
interface Bill extends HasFormatter{
id: string | number
amount: number
server: string
}
```
然後建立兩個常數
```
const user = {
id: 1,
format(): string {
return `this user has a id of ${this.id}`
}
}
const bill = {
id: 2,
amount: 50,
server: 'mario',
format(): string {
return `Bill with id ${this.id} has E ${this.amount} to pay`
}
}
```
最後下面加上兩個function
```
function printFormatted(val: HasFormatter): void {
console.log(val.format())
}
function printBill(bill: Bill): void{
console.log(`server: ${bill.server}`)
console.log(bill.format())
}
printFormatted(user)
printFormatted(bill)
printBill(bill)
printBill(user) #NOT ALLOW
```
可以發現PrintFormatted 可以使用bill因為bill也是他的部分
但printBill 不能用user,因為他沒有bill的其他attribute
### Extending Type Alias
我們可以使用 & 來擴展type
```
type Person ={
id: string |number
first: string
}
type User = Person & {
email: string
}
```
這樣User的type就如同以下
```
type User ={
id: string |number
first: string
email: string
}
```
接下來我們寫幾個variable
```
const personOne ={
id: 1,
firstName: 'mario'
}
const personTwo: User ={
id: 1,
first: 'Yoshi',
email: 'Yoshi.net@dev.com'
}
const personThree ={
email: 'peach@netninja.dev'
}
```
最後來寫進去一個FUNCTION來測試
可以發現personOne是沒有的,因為他沒有email。
personThree也是不能用的,因為他只有email
```
function printUser(user: User) {
console.log(user.id, user.email, user.first)
}
printUser(personOne) // Not Allow
printUser(personTwo)
printUser(personThree) //Not Allow
```
## Generic
一般而言一個function若要重複利用且可以回傳多個type,我們不會使用any,也不會不停的寫出不同return type的 function這會導致使用上很困難且麻煩,這時候就會使用Generic來做使用以下是三個一般 type 跟generic的寫法
```
function logAndReturnString(val: string): string {
console.log(val)
return val
}
function logAndReturnNumber(val: number ): number {
console.log(val)
return val
}
function logAndReturnBoolean(val: Boolean): Boolean{
console.log(val)
return val
}
function logAndReturnValue<T>(val: T): T {
console.log(val)
return val
}
```
同時,一個Generic也可以跟其他種類一起output
寫法如下
```
interface HasID {
id: number
}
function addIdToValue<T>(val: T):T & HasID {
const id = Math.random()
return { ...val, id }
}
```
最終也可以跟interface一起結合再輸出
```
interface Post {
title: string
thumbsUp: number
}
const post = addIdToValue<Post>({ title: 'marmite rules', thumbsUp: 250})
console.log(post.title, post.id, post.thumbsUp)
```
#### Generic Interface
可以把interface跟Generic結合
```
interface Collection<T> {
data: T[]
name: string
}
```
也可以特別做出指定的type,若沒有輸入type也會完整的顯示你輸入的那種type直接採用
```
function randomCollectionItem<T>(c: Collection<T>): T {
const i = Math.floor(Math.random() * c.data.length)
return c.data[i]
}
const result1 = randomCollectionItem<string>(collectionOne)
const result2 = randomCollectionItem(collectionTwo)
```
## Generic 限制
雖然Gneric 可以輸入任何型態,但你也可以要求他一定要繼承某種型態
可以這樣寫
下面寫法就可以知道不管怎樣他都會有HasId這種型態了。
```
interface HasId {
id: number
}
class DataCollection<T extends HasId> {
constructor(private data: T[]){}
loadOne(): T {
const i = Math.floor(Math.random() * this.data.length)
return this.data[i]
}
loadALL(): T[] {
return this.data
}
add(val: T): T[] {
this.data.push(val)
return this.data
}
deleteOne(id: number): void {
this.data = this.data.filter((item) => item.id !== id)
}
}
```
## Web 開發使用TS
我們所知道我們現在都使用JS在前端框架來做開發,但我們在TS使用的時候若是要先轉成js再輸出包裝,這都是非常耗時間的行為
所以我們就會下載來使用parcel來做相關的處理直接把ts code跟html連接與打包。
記得html body tag關閉之前,使用parcel都要記得把ts檔案跟html連結
記得要寫下面的script
```
<script src="./ts/index.ts" type="module"></script>
```
也附上原文
```
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="./styles/index.css" />
<title>Homepage</title>
</head>
<body>
<nav>
<h1>Pizza Palace Menu Editor</h1>
<a href="create.html" class="btn">Add new Pizza</a>
</nav>
<main class="root wrap">
<!-- menu content goes here -->
</main>
<script src="./ts/index.ts" type="module"></script>
</body>
</html>
```
接著我們可以把parcel加入dev其中,可以使用npx指令把他打包
```
npx parcel src/*.html
```
### return type
typescript中若是想回傳或是輸入dom的資料格式,就要寫Element
```
function rednerTemplates(templates: string[], parent: Element): void {
const templateElement = document.createElement('template')
for (const t of templates) {
templateElement.innerHTML += t
}
parent.append(templateElement.content)
}
```
有時候寫東西回傳的會是null會沒辦法處理,若要避免他們成為NULL(若是你要回傳string 但ts會判定為string | null),最後面加上!就好
```
const rootElement = document.querySelector('.root')!
```
使用ts在dom上
```
import { PizzaProps, Pizza } from "./models/Pizza"
const titleInput = document.querySelector(
'input[name="title"]'
) as HTMLInputElement
const descriptionInput = document.querySelector(
'textarea'
) as HTMLTextAreaElement
const form = document.querySelector('.create') as HTMLFormElement
form.addEventListener('submit', async(e) =>{
e.preventDefault()
const data = new FormData(form)
const newPizza: PizzaProps = {
title: data.get('title') as string,
description: data.get('description') as string,
toppings: data.getAll('toppings') as string[],
price: parseInt(data.get('price') as string)
}
const res = await Pizza.save(newPizza)
if(!res.ok) {
console.log('not able to save the pizza')
}
if (res.ok) {
window.location.href ='/'
}
})
```
## Next Js 實作
書寫的時候常常會有一些寫法讓component可以被使用但有時太過複雜就會寫成下面這樣,主要還是postcard的props可以被放入title跟author他們的type為string
```
export default function PostCard({
title,
author,
}: {
title: string
author: string
}) {
return (
<div className='card'>
<h2>{title}</h2>
<p>Written by {author}</p>
</div>
)
}
```
當然也可以利用interface 寫成下面這樣,更加方便
```
interface PostCardProps {
title: string
author: string
}
export default function PostCard({
title,
author,
}: PostCardProps) {
return (
<div className='card'>
<h2>{title}</h2>
<p>Written by {author}</p>
</div>
)
}
```
若要把interface寫出來讓大家共用,有時候會寫一個types.d.ts的檔案,讓所有人都可以拿到這個公開資料檔案的interface可以去繼承跟實作
postcard.tsx
```
interface PostCardProps {
post: Post
}
export default function PostCard({post}: PostCardProps) {
return (
<div className='card'>
<h2>{post.title}</h2>
<p>Written by {post.body}</p>
</div>
)
}
```
page.tsx
```
import PostCard from "@/components/PostCard"
const fetchPosts = async(): Promise<Post[]> => {
const res = await fetch('https://jsonplaceholder.typicode.com/users/1/posts')
if(!res.ok) {
console.log('could fetch the post ')
}
return res.json()
}
export default async function Home() {
const posts = await fetchPosts()
return (
<main>
<h2>Home</h2>
{posts.map((post) =>(
<PostCard key={post.id} post={post} />
))}
</main>
)
}
```
type.d.ts
```
interface Post {
id: number
title: string
body: string
}
```
## Set in typescript
一般的set
```
const names = new Set<string>()
names.add('mario')
names.add('peach')
names.add('luigi')
names.add('mario')
console.log(names)
```
custom interface with set
```
nterface User {
email: string
score: number
}
const user1:User = { email:'mario@gmail.com', score: 50 }
const user2:User = { email:'apple@gmail.com', score: 80 }
const users = new Set<User>()
users.add(user1)
users.add(user2)
users.add(user1)
console.log(users)
```
Set with function
```
function logUserEmails(users: Set<User>): void {
users.forEach((user) => console.log(user.email))
}
logUserEmails(users)
```
## Enum
有時候我們寫TS會寫下面這function
```
function addTicket(details: string, Priority: number){
}
```
但你的number有時候是表達先後順序或是大小,但你沒有定義的話就不知道數字含義,所以可以使用Enum
```
enum Priority {
Lowest = 0,
Low = 1,
Medium = 2,
High = 3,
Urgent = 4,
}
```
這樣就可以把function寫成
```
function addTicket(details: string, priority:Priority) {
if (priority === 0 ) {
}
if (priority === 1 ) {
}
if (priority === 2) {
}
if (priority === 3) {
}
}
```
甚至輸出或是if else也可以寫成這樣
```
function addTicket(details: string, priority:Priority) {
if (priority === Priority.Urgent ) {
}
if (priority === 1 ) {
}
if (priority === 2) {
}
if (priority === 3) {
}
}
```