TypeScriptSTUDY _ 4장_ 타입 확장하기 . 좁히기 [ 4.2 타입 좁히기 - 타입 가드 ]

zeroha·2024년 12월 2일
0

TypeScriptStudy

목록 보기
11/32
post-thumbnail

4.2 타입 좁히기 - 타입 가드

타입좁히기
: 변수 또는 표현식의 타입 범위를 더 작은 범위로 좁혀나가는 과정

  • 더 정확하고 명시적인 타입 추론 가능
  • 복잡한 타입 -> 작은 범위로 축소 : 타입 안정성 높아짐

.
.
.

1. 타입 가드에 따라 분기 처리하기

타입스크립트에서의 분기 처리
: 조건문과 타입 가드를 활용하여 변수나 표현식의 타입 범위를 좁혀 다양한 상황에 따라 다른 동작을 수행하는 것.

타입가드
: 런타임에 조건문을 사용 -> 타입 검사, 타입 범위를 좁혀주는 기능

  • 스코프 scope에서 특정 타입을 조건을 만들어 분기 처리하고 싶을 때
    : " 에러 타입을 할당한다 " = 조건으로 검사하려는 타입보다 넓은 범위를 갖고 있음.

스코프
: 변수와 함수 등의 식별자identifier가 유효한 범위
( 변수와 함수를 선언하거나 사용할 수 있는 영역 )

ex) A | B 타입의 매개변수를 받을 때

if문을 사용해서 처리?
: 컴파일 시 타입 정보는 모두 제거 -> 런타임에 존재하지 않음 -> 타입 사용 조건문 제작 x

=> 컴파일해도 타입 정보가 사라지지 않을 방법을 사용해야 한다.

틀정 문맥 안 타입스크립트가 해당 변수를 타입 A로 추론하도록 유도 & 런타임에서도 유효해야 함. -> 이때 타입가드를 사용

  • 타입가드 구분

1) 자바스크립트 연산자를 활용한 타입 가드
: typeof, instanceof, in과 같은 연산자 사용
제어문으로 측정 타입 값을 가질 수 밖에 없는 상황을 유도
-> 자연스레 타입 좁히기 & 런타입에 유효

2) 사용자 정의 타입 가드
: 사용자가 직접 어떤 타입으로 값을 좁힐지 직접 지정


2. 원시 타입을 추론할 대 : typeof 연산자 활용하기

typeof 연산자 활용 -> 원시 타입에 대해 추론

  • typeof A === B 조건 분기 처리 -> 해당 분기 내 A의 타입이 B로 추론

  • 복잡한 타입 검증에는 한계
    -> typeof 연산자는 주로 원시 타입을 좁히는 용도로만 사용할 것을 권장.

const replaceHyphen: (date: string | Date) => string | Date = (date) => {
  if (typeof date === "string") {
    // 이 분기에서는 date의 타입이 string으로 추론된다
    return date.replace(/-/g, "/");
}
  return date;
};

3. 인스턴스화된 객체 타입을 판별할 때 : instanceof 연산자 활용하기

  • selected 매개변수가 Date인지 검사한 후 Range 타입의 객체를 반환할 수 있도록 분기 처리.
interface Range {
  start: Date;
  end: Date;
}

interface DatePickerProps {
  selectedDates?: Date | Range;
}

const DatePicker = ({ selectedDates }: DatePickerProps) => {
const [selected, setSelected] = useState(convertToRange(selectedDates));
  //...
};

export function convertToRange (Selected?: Date, Range): Range | undefined { 
  return selected instanceof Date 
    ? { start: selected, end: selected }
  : selected;
}

typeof 연산자 : 주로 원시 타입 판별
instanceof 연산자 : 인스턴스화된 객체 타입을 판별하는 타입 가드로 사용

A instanceof B

A : 타입을 검사할 대상 변수
B : 특정 객체의 생성자

  • A의 프로토타입 체인에 생성자 B가 존재하는지 검사해서
    존재하면 : true
    존재 안하면 : false

유의) A의 프로토타입 속성 변화 에 따라 instanceof 연산자의 결과가 달라질 수 있음.

const onKeyDown = (event: React.KeyboardEvent) => {
  if (event.target instanceof HIMLInputElement && event.key === "Enter") {
    // 이 분기에서는 event.target의 타입 HTMLInputELement 이며
    // event.key가 'Enter'이다.
    event.target.blur();
    OnCTABut tonClick(event);
  }
};

: HIMLInputElement에 존재하는 blur메서드 사용하기 위해 event.target이 HIMLInputElement의 인스턴스인지를 검사하는 준기 처리하는 로직


4. 객체의 속성이 있는지 없는지에 따른 구분 : in 연산자 활용하기

in연산자는 객체에 속성이 있는지 확인 -> true or false 반환
( 속성이 있는지 없는지에 따라 객체 타입을 구분 )

A in B

A라는 속성이 B객체에 존재하는 지 검사
true : 프로토타입 체인으로 접근할 수 있는 속성
false : delete연산자를 사용하여 객체 내부에 해당 속성을 제거해야 함.
( B 객체에 존재하는 A 속성에 undefined 할당한다고 false x )

interface BasicNoticeDialogProps {
  noticeTitle: string; 
  noticeBody: string;
}

interface NoticeDialogWithCookieProps extends BasicNoticeDialogProps {
  cookieKey: string; 
  noForADay?: boolean; 
  neverAgain?: boolean;
}

export type NoticeDialogProps =
| BasicNoticeDialogProps
| NoticeDialogWithCookieProps;

NoticeDialog 컴포넌트 : BasicNoticeDialogProps( 2가지 객체 타입의 유니온 타입 )를 props로 받음.

  • NoticeDialog 컴포넌트가 props로 받는 객체 타입이 BasicNoticeDialogProps인지 NoticeDialogWithCookieProps이지에 따라 렌더링이 달라지게 하려면.
    -> props 타입에 따라 렌더링하는 컴포넌트를 분기처리하면 된다.

NoticeDialogProps ( 유니온 타입 ) : BasicNoticeDialogProps 객체 타입 + NoticeDialogWithCookieProps 객체 타입

  • NoticeDialogWithCookieProps : BasicNoticeDialogProps 상속 & cookieKey 속성을 가짐.

=> 두 객체 타입을 cookieKey 속성을 가졌는지 아닌지에 다라 -> in 연산자로 조건 만들 수 있음

const NoticeDialog: React.FC <NoticeDialogProps> = (props) => {
  if ("cookieKey" in props) return <NoticeDialogWithCookie {..props}/>; 
  return <NoticeDialogBase {props} />;
};

in 연산자 : 런타입의 값만을 검사, 타입스크립트에서는 객체 타입에 속서이 존재하는지를 검사.
if문 스코프 : 타입스크립트는 props 객체를 cookieKey속성을 간는 객체 타입인 NoticeDialogWithCookieprops로 해석.

얼리 리턴 Early Return했기 땜에 if문 스코프 밖에 위치하는 return문의 props 객체는 BasicNoticeDialogProps의 타입으로 해석함.

얼리 리턴 Early Return
: 특정 조건에 부합하지 않으면 바로 반환 return 하는 것.

=> 여러 객체 타입을 유니온 타입으로 가지고 있을 때 in 연산자를 사용해서 속성의 유무에 따라 조건 분기 가능


5. is 연산자로 사용자 정의 타입 가드 만들어 활용하기

: 직접 타입 가드 함수 만들기

  • 반환 타입이 타입 명제 type predicates인 함수를 정의하여 사용

타입 명제 : A is B

A : 매개변수
B : 타입

참 / 거짓의 진릿값을 반환, 반환 타입을 타입 명제로 지정 -> 반환 값이 참일 때 A 매개변수의 타입을 B 타입으로 취급하게 됨.

타입 명제 type predicates
: 함수의 반환 타입에 대한 타입 가드를 수행하기 위해 사용되는 특별한 형태의 함수

const isDestinationCode = (x: string): x is DestinationCode =>
destinationCodeList.includes(x);
  • isDestinationCode
    : string 타입의 매개변수가 destinationCodeList 배열의 원소 중 하나인지를 검사하여 boolean을 반환하는 함수
    : 함수의 반환값을 boolean이 아닌 isDestinationCode로 타이핑하여 타입스크립트에게 이 함수가 사용되는 곳의 타입을 추론할 때 해당 조건을 타입 가드로 사용하도록 알려줌.
  • isDestinationCode 함수 사용 예시
    : 반환값의 타입이 boolean인 것 vs is를 활용 한 것
const getAvailableDestinationNameList = async (): Promise <DestinationName[]> => {
  const data = await AxiosRequest<string[]>("get", ".../destinations");
  const destinationNames: DestinationName[] = [];
  data?.forEach((str) => {
    if (isDestinationCode(str)) {
     destinationNames.push(DestinationNameSet[str]);
      /*
isDestinationcode의 반환 값에 is를 사용하지 않고 boolean이라고 한다면 다음 에러가 발생한다.
- Element implicitly has an any type because expression of type 'string'
can't be used to index type 'Record <"MESSAGE PLATFORN"|"COUPON PLATFORM" | "BRAZE",
"통합메시지플랫폼" | "쿠폰대장간" | "braze">'
*/
    }
  });
  return destinationNames;
};

if문 내 isDestinationCode 함수로 data의 strdl isDestinationCode


도서참조 : 우아한 타입스크립트 with 리액트

profile
하 영

0개의 댓글

관련 채용 정보