설치
creact react app 로 typescript로 만들어진 템플릿을 설치해줍니다.
npx create-react-app my-app --template typescript
함수 선언식 Component
함수형 component를 만드는데는 대게 화살표 함수형으로 만들곤 했었습니다.
이때 사용할 수 있는 타입은 다음과 같습니다.
type CustomComponentProps = {
}
const CustomComponent: React.FC<CustomComponentProps> = () => {
return <div></div>
}
React.FC 타입을 사용하게 되면 장 단점이 있습니다.
장점으로는 children이나 static 컴포넌트 프로퍼티들 ( defaultProps, propTypes, contextTypes, displayName ) 이 자동 완성이 지원된다는 점입니다.
하지만 단점으로는 다음과 같습니다.
- defaultProps 설정이 유효하지 않다는 점입니다. defaultProps 설정을 해도 컴포넌트를 사용할 시에 해당 props를 전달하지 않으면 컴파일 에러가 뜹니다.
- children 이 "?:(optional)" 으로 들어가 있기 때문에 명시적으로 children이 들어가야 하는지 아니면 안들어가도 되는지를 알 수 가 없습니다.
하여 함수형 컴포넌트를 만들 때는 다음과 같이 만듭니다.
type CustomComponentProps = {
}
function CustomComponent({}: CustomComponentProps) {
return <div></div>
}
export default CustomComponent
Component props type
component의 props의 타입을 지정해 두면 component를 사용할 시에 코드 어시스트의 혜택을 얻을 수 있습니다.
또한 유지보수시 잘못된 props를 넣거나 다른 타입의 props를 할당하는 것을 방지 할 수 있습니다.
이때 interface 또는 type alias 방법을 사용할 수 있습니다.
type alias 와 interface 차이점
- class에 implement 문을 작성할 시 type alias 도 가능하지만 "union" 문이 들어간 type alias는 허용하지 않습니다.
- interface에 extends 문을 작성할 시 type alias 도 가능하지만 "union" 문이 들어간 type alias는 허용하지 않습니다.
- type alias 는 declaration merging 이 되지 않습니다.
대게 React와 props와 state를 type alias로 타입 설정 하길 권장합니다. 하여 여기서 기본적으로는 type alias 방법을 사용합니다. 이유는 다음과 같습니다 .
- 더 적은 타이핑
- 일관성 있는 문법 제공 ( interface와 type alias로 된 모든 것들을 intersection으로 묶을 수 있습니다. )
- React 컴포넌트를 사용자들이 재 선언을 해서 사용(몽키패칭)할 필요가 없습니다. 따라서 interface의 declaration merging 기능이 필요가 없습니다. ( 대게 컨벤션으로 interface는 라이브러리를 제작할때 공공 API를 위해 사용하거나 서드파티를 위해 엠비언트 타입을 정의할때 사용합니다. )
type alias 를 사용했을 시에 예시는 다음과 같습니다.
-
defaultProps를 지정시에 해당 타입을 typeof로 합칠 수 있습니다.
- 다음과 같이 하면 CustomComponent 를 사용할 시 name props은 optional props로 나타나게 되어 더 명시적으로 디폴트 값이 있다는 것을 알려줄 수 있습니다.
type CustomComponentProps = { age: number } & typeof defaultProps const defaultProps = {name: 'merlin'} function CustomComponent({name}) { } CustomComponent.defaultProps = defaultProps
- rebass 의 컴포넌트 Props는 interface로 되어 있지만 type의 intersection 으로 컴포넌트의 props를 extends 할 수 있습니다.
import React from "react"; import { Box, BoxProps } from "rebass"; import { iconCollection } from "./Icon.styled"; import { IconType } from "types"; type IconProps = BoxProps & { icon: IconType; children?: never; } function Icon({ icon, ...rest }: IconProps) { return <Box sx={iconCollection[icon]} {...rest}></Box>; } export default Icon;
자주 사용 한 typescript 문법
- keyof
interface CustomObject {
name: 'merlin'
address: 'address'
}
// CustomObjecyKeyType = 'name' | 'address'
type CustomObjectKeyType = keyof CustomObject
// CustomObjectValueType = 'merlin' | 'address'
type CustomObjectValueType = CustomObject[keyof CustomObject]
- Exclude
type CustomObjectValueType = 'merlin' | 'address' | 'age' | 'info'
// FilterCustomObjectValueType = 'merlin' | 'address'
type FilterCustomObjectValueType = Exclude<CustomObjectValueType, 'info' | 'age'>
- Pick
interface CustomObject {
name: 'merlin'
address: 'address'
age: 'age'
info: 'info'
}
/* {
name: 'merlin'
address: 'address'
} */
type PickCustomObjectType = Pick<CustomObject, "name" | "address">;
typescript + react 장점
- component 에 props로 무엇이 있는지 그 props 값으로는 무엇이 들어가야 하는지 빼먹진 않았는지 알 수 있습니다.
- 복잡한 API 응답 타입도 미리 지정해 두면 코드 어시스트가 작동되어 API 응답 값에 무엇이 있었는지 쉽게 알 수 있습니다.
- hooks 을 destructure 시에 어떤 타입이 나오는지 쉽게 알 수 있습니다.
참고
https://medium.com/@martin_hotell/interface-vs-type-alias-in-typescript-2-7-2a8f1777af4c