전에 근무하던 회사에서는 자바스크립트를 사용했다보니
타입스크립트는 안 쓴지 시간이 조금 지났다
그래서 이번 기회에
예전에 했던 타스 + 리액트 프로젝트를 되짚어보며 찬찬히 정리해보려고 함!
문자열
let name: string = "청포도에이드";
숫자
let today: number = 28;
배열
let arr1: number[] = [1, 2, 3];
let arr2: Array<number> = [1 , 2, 3];
let arr3: Array<string> = ["청포도", "에이드"];
let arr4: [string, number] = ["writer", 27];
객체
let grape: object = { name: "청포도에이드", age: 27 };
object로 타입 지정 후 안에 요소는 마음 대로 추가할 수 있음
그러나 위의 경우 자유도가 너무 높아지기 때문에 ts를 쓰는 의미가 없기에
아래처럼 사용하는 것을 추천
let info: { name: string; age: number } = {
name: "ade",
age: 27
};
불리언
let check: boolean = true;
함수
a, b라는 숫자를 받아 합을 구하는 함수를 작성한다면?
function 함수(a: number, b: number): number {
return a + b;
}
위처럼 적어준다. 이 때 소괄호 안에 매개변수의 타입도 지정해주어야하며, 소괄호와 중괄호 사이엔
return 값의 타입을 지정해주어야한다.
그렇다면 return이 없는 함수는 어떡함?
function 함수(a: number, b: number): void {
console.log("return 없음");
}
void를 적어주면 된다 ^_^
그럼 매개변수를 선택적으로 받아야하는 상황에선 어떡함?
특정 매개변수가 필요없을 수도 있잖아요
function optionalFun(a: string, b?: string): string {
if(b){
return a+b
}else{
return a
}
}
해당 매개변수 뒤에 ? 를 적어주면 된다.
그럼 b는 있어도 그만 없어도 그만
미리 타입 지정하기
type vs interface
두 가지 모두 ts의 타입을 지정할 때 사용하는 타입 선언자(?)이다
뭘 쓰든 크게 차이는 없는 듯하고 메서드만 조금 다르다
진행하는 프로젝트 코딩 컨벤션에 맞춰 개발하면 됨
interface
User라는 타입을 만들어보자.
참고로 type 변수의 맨 앞 글자는 대문자로 적는 게 관례이다.
interface User {
name: string;
age: number;
}
,(콤마)아니고 ;(세미콜론)임 주의!!
이제 얘를 활용한다면?
const writer: User = { name: "청포도", age: 27 }
타입을 지정하지 않고 썼다면
let writer: { name: string; age: number } = {
name: "청포도",
age: 27
};
이런 식으로 적었어야 하는데
interface로 타입을 따로 정의 해두어서 가독성이 좋아졌다
그리고 프로젝트 크기가 커질 수록 같은 타입을 쓸 일이 굉장히 많아지는데
이렇게 한 번 타입을 지정해두면 반복적인 코드를 상당히 많이 줄일 수 있다.
근데 함수에서도 쓸 수 있음?
ㄴ ㅇㅇ
function getUser(user: User) {
console.log(user);
}
getUser({ name: "청포도", age: 27 });
ES6 애로우 함수 (화살표 함수)에서도 쓸 수 있음?
ㄴ ㅇㅇ
interface Plus {
(x: number, y: number): number;
// 순서대로 매개변수 타입, 매개변수2 타입, return값 타입
}
let plusFun: Plus = (a, b) => a + b;
plusFun(1,4)
배열에 쓰기
interface StringArr {
[index: number]: string;
}
let arr: StringArr = ["하", "이", "하", "이"];
객체에 쓰기
interface Info {
[key: string]: string;
}
const info: Info {
name: "청포도",
hobby: "running"
}
객체 안에 속성이 몇 개 들어가냐에 관계 없이
대괄호 하나로 퉁칠 수 있어서 편함!
이미 지정해 높은 interface에 요소 몇 개만 추가한 또다른 interface를 쓰고 싶으면 어떻게 함? 하나 더 만듦?
ㄴ ㄴㄴ 그럼 반복되는 코드가 많아져서 개발자들이 싫어함.
extends를 쓰면 됨ㅇㅇ
interface Old {
name: string;
age: number;
}
interface New extends Old {
job: string;
}
// 대충 Old의 정신을 받아... New로 발전시키겠다는 뜻
const info: New = {
name: "청포도",
age: 27,
job: "developer"
};
이렇게 쓰면 날먹 가능
interface는 이제 끝
type으로 가보자
Type
필자는 개인적으로 type이 더 좋음. 이유는 개날먹 메서드가 존재하기 때문이다. 그건 후반에 설명하겠음
타입 선언하기
직관적이어서 좋은 듯
type something = string | number;
//그냥 js let, const, var랑 똑같이 사용 가능
const str1: something = "hello";
const str2: something = 123132;
타입을 확장하는 여러가지 방법
1. interface + interface = type
interface Old1 {
name: string;
age: number;
}
interface Old2 {
name: string;
job: string;
}
type New = Old1 & Old2;
let info: New = {
name: "청포도",
age: 27,
job: "dev"
};
2. type + type = type
type Info1 = { name: string; age: number };
type Info2 = { age: number; hobby: string };
type Info3 = Info1 & Info2
3. type + 직접 지정 = type
type Info1 = { name: string; age: number };
type Info2 = { age: number; hobby: string };
type Info3 = Info1 & {
hobby:string
}
이렇게 하면 interface에서 extends로 확장하던 걸
& 딸깍 하나로 날먹할 수 있다.
그리고 대망의 type의 개날먹 매우 효율적인 메서드를 알아보자!
type Info = {
name: string;
age: number;
hobby: string;
address:string;
};
이런 type이 있다 쳐보자...
근데 아... 나는 address없는 타입 하나 만들고 싶은데...
반복 코드 적긴 싫은데... 할 때!!!
type InfoWithoutAddress = Omit<Info, "address">
이렇게 쓰면 address만 뺀 Info 타입이 짜잔하고 생긴다
위의 경우는 안 쓸 거 같지만 생각보다 많이 쓰인다.
리액트를 쓰다보면 props로 함수부터 변수... 뭐 이것저것 많이 내려보내주는데
이 때 굳이 자식 컴포넌트에서 쓰지 않는 정보는 보내줄 필요가 없다
근데 그러면 자식 컴포넌트에서 쓰지 않는 정보가 빠진 새로운 type을 또 새로 정의해줘야한다.
개발을 하다보면
타입을만들었다가뺐다가없앴다가다시만들었다가버전1만들었다가2만들었다가......
하는 일이 많이 생기기 때문에 여러가지로 유용한 메서드이다
사실 아래처럼 사용해도 된다. ES6 옵셔널 체이닝 쓸 때처럼...
물음표 하나 붙이면 된다. address는 존재할 수도 있고 없을 수도 있다는 뜻이다.
type Info = {
name: string;
age: number;
hobby: string;
address?:string;
};
하지만 Omit을 쓰는 게 typescript 의 취지에 더 맞아보이고 위험도 역시 낮다.
아니 근데 타입 형식 하나만 빼는 거 말고 하나의 형식만 가져오는 건 없음?
ㄴ 없었으면 글 쓰지도 않았다
type Info = {
name: string;
age: number;
hobby: string;
address:string;
};
여기서 name 속성만 가져와서 새로운 타입을 지정해보겠음
type OnlyName = Pick<Info,"name">
이게 끝.... ;;;
사실상 이것때문에 글 썼다고 해도 과언이 아님
그럼 목적을 이루었으니
이제 남은 것들을 차차 정리해보겠습니다.
Class로 타입 선언
class Info {
// constructor 위에 선언
private name: string;
public age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
constructor 위에 변수 선언해주고
constructor 안에서 this << 적어주는 것 주의
아 근데 다 알겠는데.. 들어올 변수의 타입을 확정할 수 없으면 어떡함? API data가 매번 다른데?
ㄴ 그것 또한 방법이 있다
제네릭을 활용하자
<T>와 같이 타입을 선언한다. 알파벳은 대부분 T를 사용한다.
type Response<T> = {
data:T[],
totalPage:number
page:number,
};
예시 코드를 이렇게 작성한 이유가 있다
현업에선 대부분 백엔드 개발자로부터 api를 받았을 때 위와 같은 형식인데
data type이 다 다르기 때문에 매번 형식을 지정해주기엔 무리가 있다.
하지만 제네릭을 활용해주면 Response타입 하나 지정해두고 계속 우려먹으면서 쓸 수 있다.
함수에서 쓰려면?
function 뭐가들어올지모르는함수<T>(text: T): T {
console.log(text);
return text;
}
뭐가들어올지모르는함수<string>('Hello World!');
그러니까 이 방법은 !! 딱 사용할 때에 맞춰서 type을 즉시 선언해주고 사용할 수 있는 방법이다!
interface로도 가능??
ㄴ ㅇㅇ 가능
interface Info<T> {
name: T;
job: string;
}
const grape: Info<string> = { name: '청포도', job : developer };
이런 식으로 사용하면 된다~~
함수에서 쓰는 방법
function printText<T>(txt: T[]): T[] {
console.log(txt.length);
return txt;
}
printText<string>(['welcome', 'to', 'the', 'show']);
제네릭 타입 제약조건
interface TextType {
length: number;
}
function countTextLen<T extends TextType>(text: T): T {
console.log(text.length);
return text;
}
countTextLen("welcome to the show!"); // 20
countTextLen({ length: 123 }); // 123
T extends TextType은 제네릭 타입의 제한 조건을 의미함
T는 반드시 TextType을 상속하거나 구현하는 타입이어야 함.
즉, T 타입은 반드시 length 속성을 가져야 하며, 그 값은 숫자여야 함.
가능한 다른 예제
countTextLen([1, 2, 3, 4]);
// 출력: 4
// 반환: [1, 2, 3, 4]
const obj = { length: 10, value: "example" };
countTextLen(obj);
// 출력: 10
// 반환: { length: 10, value: "example" }
length 속성을 가지고 있는 string, array, object 모두 가능하다.
제약 조건에 맞지 않는 경우
countTextLen(123);
// 오류: Type 'number' does not satisfy the constraint 'TextType'.
const invalidObj = { length: "not a number" }; // 오류
countTextLen(invalidObj);
// 오류: Type 'string' is not assignable to type 'number'.
keyof
interface Category {
music: number;
movie: number;
recital: number;
}
å
function countType<T extends keyof Category>(category: T): T {
return category;
}
// 'music', 'movie', 'recital'만 인자로 사용 가능
countType('price');
Category 에 있는 key 값만 인자로 사용 가능하도록 제한하는 방식이다.
다음 편은 리액트 + 타입스크립트로 돌아오겠음
'프론트엔드 > Typescript' 카테고리의 다른 글
[TS + REACT/REACT-NATIVE] 사용법 (1) | 2024.12.30 |
---|---|
[Typescript] 타입스크립트 interface (0) | 2022.06.09 |
[Typescript] 기본 설정, 세팅 (0) | 2022.06.09 |