string union type을 type guard 하는 법 (부제 typeof Array[number] 와 const assertion )
string으로 이루어진 union type을 타입가드하고 싶을 때를 알아보자
🧨 문제상황
color 의 타입을 담는 `string union type` 을 만들었다.
type ColorKey = 'red' | 'orange' | 'yellow' | 'green' | 'blue' | 'purple'
그리고 그 color값을 저장하는 state 를 만들고,
외부에서 `val string` 을 받아오면 state에 저장하는 handler 함수까지 만들었지만..
const [color, setColor] = useState<ColorKey>('red')
const onChange = (val: string) => {
setColor(val) // TS2345: Argument of type 'string' is not assignable to parameter of type '"red" | "orange" | "yellow" | "green" | "blue" | "purple"'.
}
아래와 같은 에러가 발생하고 만다.
Argument of type 'string' is not assignable to parameter of type '"red" | "orange" | "yellow" | "green" | "blue" | "purple"'.
string타입은 primitive type으로 포괄적인 의미를 가진 타입인데, ColorKey 은 정확한 값이 선언되어 있다.
즉, 의미가 넓은 string은 좁은의미인 Union type인 ColorKey에 매핑할 수 없다는 것이다.
이를 해결하기 위해, 타입스크립트에선 string으로 받아온 val 이 ColorKey임을 체크하는 Type guard(타입가드) 가 필요하다.
✨ 해결방안
1. union type이 담긴 배열을 상수배열로 선언한다.
const COLORS = ['red', 'orange', 'yellow', 'green', 'blue', 'purple'] as const
`as const` 타입단언중 하나인데, primitive type을 literal type으로 지정해주고 그 값을 readonly로 설정해준다.
TS 는 이름에서 알다시피 Type 에 대해 굉장히 예민하다.
let은 할당된 변수의 값이 변경될 수 있으므로, TS 컴파일러는 string 으로 타입을 추론한다.
let name = "Song"
// let name: string
반면에, const 로 선언한 변수는, 변수의 값이 불변하므로, 상수로 추론해 아래처럼 타입이 "Sone" 이 되어버린다.
const name = "Song"
// const name: "Song"
이처럼 TS는 const 로 선언한 변수를 상수로 추론한다.
결국 위의 `name` 은 `Song 타입` 이 된것인데, 이처럼 정확한 값이 지정된 타입을 `literal type` 이라 한다.
`as const` 는 위의 const 예시처럼 특정변수를 상수로 만들어주는 선언이다.
const assertion 이라 한다.
다시 정리하자면, 위의 배열을 const 로 선언하면, 'red' , 'orange' 'yellow' 'green' 'blue' 'purple' 이 담긴 상수배열로 선언된다.
당연히 const assertion 을 하지 않는다면, COLORS의 타입은 string[] 이 된다.
TS 컴파일러는 COLORS 배열을 이제 `readonly ['red' , 'orange' 'yellow' 'green' 'blue' 'purple']` 으로 추론하게 된다.
2. index signature 를 이용해 타입을 만들자
type ColorKey = typeof COLORS[number]
`typeof Array[number]` 을 사용해 배열 안에 있는 숫자로 이루어진 모든 index값을 가져와 union type으로 만들어준다
typeof
타입을 반환해준다.
시각적으로 주어진 값이 어떤 타입인지 반환할 때 사용하는 자바스크립트의 런타임 typeof 연산자와는 차이가 있다.
const original = {
name: "movie",
title: "My love"
}
let adaptation: typeof original // original 의 타입을 반환해 adaptation의 타입이 된다.
`typeof COLORS` 은 `Array<"red" | "orange"|...>` 타입을 반환한다.
목록 유형과 같은 배열은 number index를 가지고 있는데, 특히나 `typeof COLORS[number]` 는 Array 의 특정 Index의 타입을 반환해준다.
즉 union type이 된다.
3. 타입가드 함수를 만든다.
const isColor = (value: string): value is ColorType => {
return COLORS.includes(value as ColorType)
}
const [color, setColor] = useState<ColorKey>('red')
const onChange = (val: string) => {
if(isColor(val)) {
setColor(val)
}
}
위처럼 해결할 수 있게 된다.
📄 참고자료
https://www.qualdesk.com/blog/2021/type-guard-for-string-union-types-typescript/
'🌳Frontend > typescript' 카테고리의 다른 글
타입도 분기처리 할 수 있나요? 타입스크립트에서 조건부 타입 알아보기 (0) | 2024.02.16 |
---|---|
[Typescript/TS] 배열에서 union type 만들기 (0) | 2023.07.13 |
[타입스크립트/Typescript] clean code 클린코드 (0) | 2023.03.21 |
string union type을 type guard 하는 법 (부제 typeof Array[number] 와 const assertion )
string으로 이루어진 union type을 타입가드하고 싶을 때를 알아보자
🧨 문제상황
color 의 타입을 담는 string union type
을 만들었다.
type ColorKey = 'red' | 'orange' | 'yellow' | 'green' | 'blue' | 'purple'
그리고 그 color값을 저장하는 state 를 만들고,
외부에서 val string
을 받아오면 state에 저장하는 handler 함수까지 만들었지만..
const [color, setColor] = useState<ColorKey>('red')
const onChange = (val: string) => {
setColor(val) // TS2345: Argument of type 'string' is not assignable to parameter of type '"red" | "orange" | "yellow" | "green" | "blue" | "purple"'.
}
아래와 같은 에러가 발생하고 만다.
Argument of type 'string' is not assignable to parameter of type '"red" | "orange" | "yellow" | "green" | "blue" | "purple"'.
string타입은 primitive type으로 포괄적인 의미를 가진 타입인데, ColorKey 은 정확한 값이 선언되어 있다.
즉, 의미가 넓은 string은 좁은의미인 Union type인 ColorKey에 매핑할 수 없다는 것이다.
이를 해결하기 위해, 타입스크립트에선 string으로 받아온 val 이 ColorKey임을 체크하는 Type guard(타입가드) 가 필요하다.
✨ 해결방안
1. union type이 담긴 배열을 상수배열로 선언한다.
const COLORS = ['red', 'orange', 'yellow', 'green', 'blue', 'purple'] as const
as const
타입단언중 하나인데, primitive type을 literal type으로 지정해주고 그 값을 readonly로 설정해준다.
TS 는 이름에서 알다시피 Type 에 대해 굉장히 예민하다.
let은 할당된 변수의 값이 변경될 수 있으므로, TS 컴파일러는 string 으로 타입을 추론한다.
let name = "Song"
// let name: string
반면에, const 로 선언한 변수는, 변수의 값이 불변하므로, 상수로 추론해 아래처럼 타입이 "Sone" 이 되어버린다.
const name = "Song"
// const name: "Song"
이처럼 TS는 const 로 선언한 변수를 상수로 추론한다.
결국 위의 name
은 Song 타입
이 된것인데, 이처럼 정확한 값이 지정된 타입을 literal type
이라 한다.
as const
는 위의 const 예시처럼 특정변수를 상수로 만들어주는 선언이다.
const assertion 이라 한다.
다시 정리하자면, 위의 배열을 const 로 선언하면, 'red' , 'orange' 'yellow' 'green' 'blue' 'purple' 이 담긴 상수배열로 선언된다.
당연히 const assertion 을 하지 않는다면, COLORS의 타입은 string[] 이 된다.
TS 컴파일러는 COLORS 배열을 이제 readonly ['red' , 'orange' 'yellow' 'green' 'blue' 'purple']
으로 추론하게 된다.
2. index signature 를 이용해 타입을 만들자
type ColorKey = typeof COLORS[number]
typeof Array[number]
을 사용해 배열 안에 있는 숫자로 이루어진 모든 index값을 가져와 union type으로 만들어준다
typeof
타입을 반환해준다.
시각적으로 주어진 값이 어떤 타입인지 반환할 때 사용하는 자바스크립트의 런타임 typeof 연산자와는 차이가 있다.
const original = {
name: "movie",
title: "My love"
}
let adaptation: typeof original // original 의 타입을 반환해 adaptation의 타입이 된다.
typeof COLORS
은 Array<"red" | "orange"|...>
타입을 반환한다.
목록 유형과 같은 배열은 number index를 가지고 있는데, 특히나 typeof COLORS[number]
는 Array 의 특정 Index의 타입을 반환해준다.
즉 union type이 된다.
3. 타입가드 함수를 만든다.
const isColor = (value: string): value is ColorType => {
return COLORS.includes(value as ColorType)
}
const [color, setColor] = useState<ColorKey>('red')
const onChange = (val: string) => {
if(isColor(val)) {
setColor(val)
}
}
위처럼 해결할 수 있게 된다.
📄 참고자료
https://www.qualdesk.com/blog/2021/type-guard-for-string-union-types-typescript/
'🌳Frontend > typescript' 카테고리의 다른 글
타입도 분기처리 할 수 있나요? 타입스크립트에서 조건부 타입 알아보기 (0) | 2024.02.16 |
---|---|
[Typescript/TS] 배열에서 union type 만들기 (0) | 2023.07.13 |
[타입스크립트/Typescript] clean code 클린코드 (0) | 2023.03.21 |