[2021.08.23] TypeScript

📌 함수

  • 매개변수의 우측에 콜론(:)과 매개변수의 타입을 정의
  • return 값의 타입은 소괄호()우측에 타입을 정의
    ➡ formatDate의 매개변수 d는 Date 객체 타입을 받아야하고, return 값은 문자열이어야 한다
const formatDate = (d: Date): string => { // ✅
	const year = d.getFullYear();
	const month = d.getMonth() + 1;
	const day = d.getDate();

	return `${year}${month}${day}`;
};

const today = formatDate(new Date());
console.log(today);
// lang의 경우 인자를 전달하지 않으면 "ko"라는 문자열 값이 할당
// delimiter는 물음표를 사용하여 선택적으로 인자 전달
let formatDate = (d: Date | number, lang = "ko", delimiter?: string): string => {
};

📌 새로운 타입 - 기본

💡 1. Any

  • 어떤 타입이라도 모두 지정 가능하다
  • JS로 컴파일된 코드를 보면 아무 타입도 지정되지 않은 원래의 JS 코드와 똑같다.
    ➡ any 타입을 쓰면? 타입스크립트 안 쓴 거와 같다 (any는 쓰지말자)

💡 2. void

  • void는 any와는 반대로 어떤 타입도 없다는 뜻
  • 보통 함수에서 반환값이 없는 경우 사용

💡 3. Enum

  • 언제 사용 ? 상수로된 세트 값이 필요할 때
// Category라는 이름의 enum 타입을 정의한다
// Category의 enum 값에 Past, Pizza, Dessert 가 있다.
enum Category {
	Pasta,
	Pizza,
	Dessert
}
let menuCategory: Category = Category.Pasta; // 0

// enum에 값 부여
enum Category {
	Pasta = 'pasta',
	Pizza = 'pizza',
	Dessert = 'dessert'
}
let menuCategory: Category = Category.Pasta; // pasta

💡 4. union

  • union타입은 여러 타입이 올 수 있을 때 사용
function formatDate(date: string | number | Date): string {
	// 로직 생략

	return `${year}${month}${day}`
}

formatDate(20201028);
formatDate(new Date());
formatDate('20201028');
  • 배열에 여러 타입의 값을 사용하고 싶을 때, 튜플을 쓸 때와 union을 쓸 때의 차이점
// union
let starNumberArr: (number | string)[] = [1, 2];
starNumberArr.push("3점");

// tuple
let starArr: [number, string] = [1, "1점"];
  • union은 사용하고자 하는 타입 중에 하나만 있어도 가능.
    튜플을 사용하면 배열의 요소에 정의한 타입이 모두 존재해야 한다.

💡 5. Type Alias

  • 타입을 재사용하거나, 객체를 위한 타입을 정의할 때 많이 사용
// 방법은 아래처럼 type으로 정의하거나, interface로 타입을 정의하거나.
type ID = number | string;

//checkInfo 함수의 매개변수 info 객체를 위해 Info라는 타입을 만들기.
type Info = {
  id: ID;
  pw: string;
};

function checkInfo(info: Info) {

}

let id: ID = "1010";
checkInfo({ id, pw: "password" });

📌 타입추론 (Type Inference)

  • 타입스크립트에서는 타입을 표기하지 않아도 타입스크립트 컴파일러가 변수에 할당된 값을 보고 타입을 추측
// ts 파일에서
let starArr = [1, 2, 3];
starArr.push('4점');  
// 에러: Argument of type 'string' is not assignable to parameter of type 'number'.
// starArr는 숫자로만 이루어진 배열이 할당되어, number[] 타입으로 추론됨 => 에러 발생!

📌 인터페이스 (Interface)

  • state와 props의 타입을 정의하는데 주로 사용

  • readonly, ? 키워드 사용

// readonly는 읽기만 가능한 프로퍼티로, 값을 수정할 수 없습니다.
// 프로퍼티명 다음에 물음표(?)를 사용하면 해당프로퍼티를 추가해도 되고 안해도 됩니다.
interface Restaurant {
	readonly name: string;
	star: number;
	address?: string;
}

const chroad3: Restaurant = {
	name: "취향로3가",
	star: 5,
};

chroad3.name = "chroad3";  // 에러: Cannot assign to 'name' because it is a read-only property.
chroad3.star = 4;

const sipboon: Restaurant = {
	name: "십분의일",
	star: 5,
	address: "을지로3가",
};
  • extends 사용 : 기존에 정의된 인터페이스를 확장
interface BasicInfo {
	name: string;
	star: number;
}
const sipboon: BasicInfo = {
	name: "십분의일",
	star: 5,
};

// extends
interface DetailInfo extends BasicInfo {
	address: string;
	phone: string;
	position: number[];
}
const chroad3: DetailInfo = {
	name: "취향로3가",
	star: 5,
	address: "을지로3가",
	phone: "123-456-7890",
	position: [37.565496, 126.99142],
};
  • 교차타입 : type 사용
    ➡ 합쳐서 새로운 인터페이스를 만드는 것은 아니고, & 기호를 사용하여 기존에 정의했던 인터페이스를 합쳐서 새로운 타입을 만드는 것
interface BasicInfo {
	name: string;
	star: number;
}

interface DetailInfo {
	position: number[];
}

type Info = BasicInfo & DetailInfo;

📌 타입호환성 (Type Compatibility)

Q. v1, v2 둘 중에 어떤 라인이 에러가 날까?

function getStar(score: number | string, scoreNum: number) {
  const v1: number = score;
  const v2: number | string = scoreNum;
}
  • (정답) v1
  • 만약 score가 문자열로 전달됐다면 string 타입이 되기 때문에 v1 타입인 number에 포함되지 않는다.
  • scoreNum 매개변수는 숫자타입이기 때문에 v2타입인 number, string 둘 중에 하나 포함되므로 타입이 호환된다.

📌 제네릭 (Generic)

  • 타입을 정의할 때 특정 타입으로 고정된 것이 아니라,
    내가 원하는 타입을 자유롭게 지정해서 계속 사용하고 싶을 때 제네릭을 사용
  • 제네릭은 타입을 마치 재사용할 수 있는 변수처럼 취급해서 여러 군데에서 사용하고 싶을 때 사용
  • 제네릭은 함수명 우측에 화살표괄호(<>)로 감싼다.
  • T는 특별한 의미가 없는 변수명 같다고 생각하면 되는데,
    제네릭을 사용할 때 보통 대문자 T를 자주 사용한다.
    (내가 원하는 이름으로 자유롭게 작성할 수 있다)
function makeArr<T>(el: T): T[] {
	return [el];
}

makeArr<number>(1);      // 1 number[]
makeArr<string>("1점");  // 2 string[]
makeArr<boolean>(true);  // 3 boolean[]
  • 제네릭 타입을 명시하지 않아도 컴파일러는 전달되는 인자값을 통해 타입을 추론하여 T의 타입을 결정
  • 아래 코드는 훨씬 간결하고 가독성이 높아졌지만, 추후 유지보수를 위해서나 더 복잡한 예제의 예상치 못한 오류를 위해 제네릭 타입을 정확히 써주는게 좋을 때도 있다.
makeArr(1);
makeArr("1점");
makeArr(true);

📌 리액트를 위한 타입스크립트

💡 객체 타입을 정의하는 방법은 object, interface, type alias 세 가지!

  • object 타입 → 타입을 재사용할 수 없으며, 프로퍼티가 많을 수록 가독성이 좋지 않다
  • state나 props를 정의할 때 interface나 type을 사용하는 것이 좋다.
  • type과 interface는 확장, 병합 가능성이라던지 선언 등의 차이점이 있어서 상황에 따라 적절하게 사용하면 되지만, 타입스크립트 공식 문서에는 type보다 interface를 사용하라고 되어있다.

💡 함수형 컴포넌트의 타입을 선언하는 두 가지 방법

1. 리액트의 FC 타입 사용

interface StarsProps {  // prop 타입 정의
  star: number;
}

const Stars: React.FC<StarsProps> = ({ star }) => {   //
  return ( 
    <div>
      {makeStars(star)}
    </div>
  )
}

2. 일반적인 함수정의 방식

interface StarsProps {   // prop 타입 정의
  star: number;
}

const Stars = ({ star }: StarsProps): JSX.Element => { // 
  return ( 
    <div>
      {makeStars(star)}
    </div>
  )
}

💡 Hooks

* useState

  • 특별히 타입을 명시하지 않더라도 타입추론을 통하여 타입이 지정된다.
const [like, setLike] = useState(false);

useState<boolean>(initialState: boolean | (() => boolean))
: [boolean, Dispatch<SetStateAction<boolean>>]
  • state의 초기값을 설정할 때 null을 사용하면? 유니온 타입을 사용하여 타입을 명시
// 유니온 타입으로 전달
const [like, setLike] = useState<boolean | null>(null);
  • state에 객체가 포함된 경우, 자세한 프로퍼티 타입을 알 수 있도록 아래와 같이 정의
// state 타입 정의
interface Comment {
	comment: string;
	username: string;
	date: string;
	star: number;
}
const [comments, setComments] = useState<Comment[]>([]);

* useRef

  • useState처럼 타입을 명시하지 않아도 된다.
  • 만약 코드의 명확성을 위해서 타입을 정의하고 싶은 경우, 아래와 같이 ref의 태그에 맞는 제네릭을 넘겨주면 된다.
const reviewRef = useRef<HTMLDivElement>(null); 
//로직 생략

return (
  <>
    <Review reviewTextRef={reviewRef} /> 
    <div ref={reviewRef}> 
      리뷰
    </div>
    </>
)

💡 Event Handler

  • 이벤트 핸들러 함수의 이벤트 매개변수에는 특별한 타입이 필요하다
  • 물론 매개변수에 타입을 명시하지 않아도 오류는 나지 않지만, 정확히 어떤 종류의 이벤트이고, 어느 태그에서 발생하는 것인지 써주는 것이 좋다.
  • 일반적으로 타입 이름은 "이벤트타입Event"으로 ChangeEvent, FormEvent, TouchEvent, FocusEvent, KeyboardEvent, MouseEvent, DragEvent 등이 있다.

💡 전역 변수

  • window에 외부 라이브러리 변수를 추가하려면 아래와 같이 해당 라이브러리에서 사용하는 전역 변수명을 추가하면 된다.
declare global {
  interface Window {
    kakao: any;
  }
}

좋은 웹페이지 즐겨찾기