Javascript의 단점
let a = 1
a = "1"
...
function b(num1, num2){
...
}
b(1, 2)
b("1", "2")
...
a변수의 타입은 정수형이지만 문자열로 쉽게 변경할 수 있습니다. 하지만 개발자는 a변수의 문자열 처리에 대한 로직이 없었다고 한다면? 이 사실을 운영에 배포하고 나서 수 일이 지나서 알게 되었다면 어떨까요? 펑션 b도 마찬가지로 개발자A가 정수형 파라메터만 처리하도록 설계하였지만 개발자B가 실수로 문자열 파라메터를 사용해서 소스를 커밋했습니다. 역시 오류는 바로 발견되지 않고 운영에 배포되고 시간이 흐른 후에야 발견되었습니다.
변수의 타입을 미리 선언하고 선언한 타입외의 값을 설정했을 경우 에러를 바로 확인할 수 있다면 위와 같은 문제를 해결할 수 있지 않을까요?
정답은 Typescript
let a: number = 1
a = "1". <-- 오류 발생
function b(num1: number, num2: number)
b(1, 2)
b("1", "2") <-- 오류 발생
a 변수에 : number 로 number 타입을 지정해주고 b펑션의 매개변수에도 : number을 지정해줬습니다. 이제 모두 숫자형만 처리하도록 하였고 만약 선언한 타입 number외의 타입을 사용할 경우 바로 오류를 확인할 수 있습니다.
타입스크립트의 현재(2022)와 미래
출저. 오픈서베이 트렌트 리포트 2022
좌측 챠트에는 주로 사용하는 프로그래밍 언어에 Typescript가 6위에 올라 있지만 우측의 향후 챠트에는 Javascript를 제치고 3위에 예측될 정도로 Typescript의 인기는 아주 뜨겁습니다!
그리고 이미 우리가 사용하는 Library들 대부분은 Typescript를 지원하고 있습니다.
많이 사용하는 axios를 예로 ./node_modules/axios.d.ts 파일을 열어보면 axios 오브젝트들의 타입들을 확인할 수 있습니다.
그러면 단점은?
단점으로는 타입 정의에 시간이 소요되며 타입 정의를 할 수록 소스 가독성이 떨어집니다.
Typescript 적용 전
const [value, setValue] = useState([])
const [num, setNum] = useState()
Typescript 적용 후
const [value, setValue] = useState<string[]>([])
const [num, setNum] = useState<number>()
Typescript 적용 전과 후를 비교적 간단한 소스임에도 소스의 길이도 길어지고 가독성이 떨어져 보입니다.
하지만 타입을 <string[]> 정의함으로 인해 string타입의 array만 사용해야 한다는 것을 바로 알 수 있습니다. 약간의 과장을 하자면 소스에 있는 API Document 같은 느낌이 들지 않나요? 선언된 타입을 보고 용도를 바로 알 수 있으니 아주 편리하다고 생각합니다.
제너릭이란?
출저. https://joshua1988.github.io/ts/guide/generics.html#제네릭을-사용하는-이유
위 링크를 추천합니다. (joshua님보다 잘 설명할 자신이 없습니다.. joshua님 최고!)
tsconfig 설정 확인
tsconfig.json 파일에서 strict 옵션은 true로 설정합니다. false로 설정하게 되면 Typescript를 사용 안하는것과 비슷함으로 항상 true로 설정합니다.
Typescript 활용 — React
1. 펑션 컴포넌트 Props 타입 정의
import * as React from 'react'
import { FC } from 'react'
interface Props {
name: string,
index: number
}
const Main: FC<Props> = () => {
return (
<></>
)
}
export default Main
FC는 Function Component임을 선언합니다. Props는 Interface로 Props를 정의했습니다. name은 문자열, index는 숫자형으로 선언했습니다. 즉 Main 펑션은 함수형 컴포넌트이고 인자로 문자열 속성 name과 숫자 속성 index를 받는다 라는 의미입니다.
FC에 마우스커서를 가져가면 FC의 타입정의를 확인할 수 있습니다. FC는 인자로 P를 받습니다. P는 저희가 정의한 Interface Props 로 대입되어 사용됩니다.
타입 정의를 확인할 때 마우스커서를 가져가면 타입정보를 확인할 수 있습니다. 궁금한 타입에 오른쪽마우스 커서 Go to Definition 으로 상세한 타입정보들을 확인할 수 있습니다.
타입 정의를 확인할 때 마우스커서를 가져가면 타입정보를 확인할 수 있습니다. 궁금한 타입에 오른쪽마우스 커서 Type Definition 으로 상세한 타입정보들을 확인할 수 있습니다.
React 18에서 암묵적으로 FC의 Children 이 제거되었습니다. React 18 이전에는 기본적으로 Children이 포함되어 있었는데 컴포넌트를 만들다 보면 자식 컴포넌트가 있을 수도있고 없을수도 있는데 기본적으로 Children이 포함되어 Typescript 관점에서는 타입을 잡아내기 어려운 이슈가 있었다고 합니다.
출저.https://blog.shiren.dev/2022-04-28/
2. useState
const [value, setValue] = useState<string[]>([])
const [num, setNum] = useState<number>()
value는 string[]로 타입을 정의하고 num은 number 타입을 정의했습니다.
useState를 Go to Type Definition을 하면 useState 타입에 대한 정의를 확인할 수 있습니다.
제너릭으로 타입이 정의되어 있어 <string[]>이나 <number>를 사용하면 useState 제너릭에 매치되어 처리됩니다.
3. Event
이벤트의 타입정의는 React.ChangeEvnet, React.MouseEvnet 등으로 정의될 수 있습니다.
React 의 Type Definition 확인 해보면 다양한 이벤트 타입정의를 확인할 수 있습니다.
4. Return
const returnValue = () : string[] => {
return ['A', 'B']
}
매개변수() 뒤에 타입을 정의하면 리턴 타입을 간단히 정의할 수 있습니다. 소스에서는 리턴타입을 string[]으로 정의하였고 returnValue 펑션의 리턴은 문자열 배열 값 [’A’, ‘B’] 으로 리턴되고 있습니다.
5. 상태관리 Recoil
import {atom} from 'recoil';
export interface userType {
email: string,
nickname: string
}export const userAtom = atom<userType>({
key: 'user',
default: {
email: '',
nickname: ''
},
});
interface로 userType을 정의하고 제너릭으로 atom<userType>을 선언했습니다. userType은 여러 곳에서 사용할 수 있으므로 export를 선언했습니다.
const [userAtomState, setUserAtomState] = useRecoilState<userType>(null)
useState 와 동일하게 제너릭으로 타입을 정의합니다 .userType은 export로 선언했던것을 import하였습니다.
6. 값으로 타입 선언하기
const game = {
바위: 1,
가위: 2,
보: 3
} as const;
game 객체에 as const를 선언함으로 game.바위 등의 속성은 읽기속성으로 오브젝트의 값도 수정이 불가능하게 됩니다.
type gameValueType = typeof game[keyof typeof game]
typeof : 객체를 타입형태로 변환
keyof : 타입의 키를 모아서 유니온타입으로 변환
gameValueType 정의는 = 1| 2| 3으로 game 객체의 값으로 타입을 정의하였다. gameValueType 이 여러군데에서 활용되고 값이 1| 2| 3이 아니라 3 | 4| 5 등으로 일괄 변경하고자 할 때 활용 할 수 있습니다.
7. 타입을 정의했는데 타입스크립트 오류가 날 경우
let x: number
const setting = () => {
x = 1
}
setting()
console.log("number => ", x + x);
x 변수의 초기값은 설정되어 있지 않습니다. setting()으로 값을 할당해 주지만 console.log의 x + x 에서 컴파일러는 빈값으로 + 연산을 하는것으로 생각해서 오류를 생성합니다. 이런 경우 개발자는 console.log 라인에서 x의 값이 할당되었음을 알 수 있습니다. 이럴 때 !를 사용하면 컴파일러에게 ‘x에 number 형 값이 확실히 들어가 있어 !’ 라고 알려줄 수 있습니다.
!는 변수이름 뒤에 사용했습니다. !(Definite Assignment Assertions) 는 개발자가 확신이 있을 때 사용하도록 권장합니다.
추천 블로그
https://joshua1988.github.io/ts/
끝.