프로그래밍 언어가 작동하기 전에 type을 확인하는 것이다.
const plus = (a, b) => a + b;
를 예로 들면 a와 b가 어떤 타입으로 들어갈지 JavaScript는 정해져있지 않다.
하지만 strongly-typed 언어인 TypeScript는 프로그램이 작동하기전에 데이터의 타입을 확인해줘서 좀 더 안전하게 작업을 할 수 있다.
위의 코드를 TypeScript로 바꾸면
const plus = (a:number, b:number) => a + b;
a와 b는 number 타입을 가져야 되다는 말이다.
이렇듯 마이웨이인 JavaScript와 다르게 TypeScript는 작은 실수로부터 우리를 보호해준다.
npx create-react-app my-app --template typescript
yarn create react-app my-app --template typescript
--template typescrip
를 추가해서 생성하는 것이다.npm install --save typescript @types/node @types/react @types/react-dom @types/jest
yarn add typescript @types/node @types/react @types/react-dom @types/jest
npm i styled-components
npm i --save @types/styled-components
@types 란?
매우 큰 Github repository로 유명한 npm 라이브러리를 사람들이 모여서 TypeScipt에서 사용 가능하게 type definition을 만들어놓은 저장소이다.
https://github.com/DefinitelyTyped/DefinitelyTyped
만약 TypeScript 선언이 없는 모듈을 import 한다면?
npm i --save-dev $types/[모듈 이름]
을 입력해서 확인을 하자.
Github repository 에서 검색하는건 추천하지 않는다. 대부분에 파일이 숨겨져 있기때문.
Object 형식으로 타입을 정의해서 사용할 수 있다.
interface PlayerShape {
name: string;
age: number;
};
const sayHello = (playerObj: PlayerShape) =>
`Hello ${playerObj.name} you are ${playerObj.age} years old.`;
sayHello({name: "gjeon", age:12});
sayHello({name: "hi", age:30});
propTypes 와 매우 유사하지만 interface는 TypeScript와 코드가 실행되기 전에 확인해 준다는 차이점이 있다.
위의 interface를 보면 무조건 정의된 인자가 들어와야하고 기본값이 정해져 있지 않다.
import styled from "styled-components";
interface ContainerProps {
bgColor: string;
borderColor: string;
}
const Container = styled.div<ContainerProps>`
width: 200px;
height: 200px;
background-color: ${(props) => props.bgColor};
border-radius: 100px;
border: 1px solid ${(props) => props.borderColor};
`;
interface CircleProps {
bgColor: string;
borderColor?: string;
text?: string;
}
function Circle({ bgColor, borderColor, text = "default text" }: CircleProps) {
return (
<Container bgColor={bgColor} borderColor={borderColor ?? bgColor}>
{text}
</Container>
);
}
export default Circle;
borderColor?: string
을 보면 : 옆에 ? 가 들어가 있다. 의미는 borderColor 라는 인자가 들어올 수도 있고 안들어올수도 있다는 말이다.
interface 를 두 번 사용했는데 아래 interface에서는 borderColor?:
로 값이 안들어와도 되지만 styled-components의 Container 는 borderColor:
를 필수로 필요로 하기에 에러가 발생한다. 이러한 경우 ??를 사용할수도 있다.
<Container bgColor={bgColor} borderColor={borderColor ?? bgColor}>
borderColor ?? bgColor
??은 borderColor의 값이 undefined 일 경우 bgColor 값으로 적용돼서 borderColor=bgColor
가 되어 props로 넘겨준다.
{ bgColor, borderColor, text = "default text" }
text가 비어있을 경우 "default text"가 값으로 적용되어 들어가는 것으로 javascrip이다.
TypeScript로 useState()를 사용하면 default값을 통해 타입을 정의해준다.
const [counter, setCounter] = useState(1);
default 값으로 1이 들어가서 counter는 number라고 정의된다. 그리고 setCounter도 number를 사용하는 액션이라는 것을 알 수 있다.
이처럼 초기값으로 유추하여 타입을 정의한다.
하지만 다른 타입으로 바뀌는걸 원한다면 아래와 같이 작성한다.
const [counter, setCounter] = useState<number|string>(1);
counter는 number 또는 string 이라는 의미이다.
import React, { useState } from "react";
function App() {
const [value, setValue] = useState("");
const onChange = (event: React.FormEvent<HTMLInputElement>) => {
const {
currentTarget: { value },
} = event;
setValue(value);
};
const onSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
console.log("hello", value);
};
return (
<div>
<form onSubmit={onSubmit}>
<input
value={value}
onChange={onChange}
type="text"
placeholder="username"
/>
<button>Log in</button>
</form>
</div>
);
}
export default App;
event: React.FormEvent<HTMLFormElement>
와 같이 생소한 부분이 나온다.
event의 타입도 정의해줘야 되는데 이때 'any' 타입이 올수도 있다. 'any' 타입은 어떤 타입이든 올 수 있다는 말인데, 가능한한 any 타입은 배제하고 이제 무슨 타입인지를 쓰거나 설명하고자 노력해야 한다고 한다.
이때 event에 타입을 추가하는 방식이
React.FormEvent<HTMLFormElement>
이다.
상황에 따라 어떤 종류의 Element에 따라 이벤트가 실행될지를 나타낸 것이다.
event 타입을 정의해주는 것을 제외한 나머지는 JavaScript + React 와 같다.
ReactJs 내의 TypeScript로 작업을 할때 어디에 타입이 있는지 어떤 타입을 넣어야 되는지 구글링을 많이 해야된다.React 이벤트 가이드
themes 를 사용할 때 타입을 정의하는것을 해보자.
styled-components theme과 typescript를 연결하는 지시를 따라한다.
Create a declarations file
https://styled-components.com/docs/api#create-a-declarations-file
선언 파일을 만들어준다. (.d.ts로 끝남)
//styled.d.ts
import "styled-components";
declare module "styled-components" {
export interface DefaultTheme {
textColor: string;
bgColor: string;
btnColor: string;
}
}
이렇게 생성한 파일은 우리가 이전에 @types/styled-components를 통해 설치한 index.d.ts 파일을 확장한다.
//theme.ts
import { DefaultTheme } from "styled-components";
export const lightTheme: DefaultTheme = {
bgColor: "white",
textColor: "black",
btnColor: "tomato",
};
export const darkTheme: DefaultTheme = {
bgColor: "black",
textColor: "white",
btnColor: "teal",
};
병합된 DefaultTheme를 import해서 불러온뒤 타입으로 정의해서 사용한다.
//index.tsx
import React from "react";
import ReactDOM from "react-dom";
import { ThemeProvider } from "styled-components";
import App from "./App";
import { darkTheme, lightTheme } from "./theme";
ReactDOM.render(
<React.StrictMode>
<ThemeProvider theme={darkTheme}>
<App />
</ThemeProvider>
</React.StrictMode>,
document.getElementById("root")
);
나머지는 javascript에서 theme을 사용하는 것과 같다.
declaration merging(병합 선언)
https://www.typescriptlang.org/docs/handbook/declaration-merging.html
https://nomadcoders.co/react-masterclass
https://create-react-app.dev/docs/adding-typescript/
https://dev.to/rajuashok/create-styled-d-ts-to-make-typescript-work-with-styled-components-5dk4