[TS] TS 기초지식2

개발 log·2021년 12월 31일
0

TS 지식

목록 보기
2/15
post-thumbnail

TS 타입

앞선 글에 설명하지 않았던 TS타입에 대해 더 깊게 적어본다.

타입설명
unknown최상위 타입
never하위 타입
객체 리터럴{ property: Type } 등등
void리턴 타입으로 사용하기 위해 의도된 undefined의 서브타입
T[]수정가능한 배열들, Array<T>로도 사용 가능
[T, T]고정된 길이지만 수정 가능한 튜플
(t: T) => U함수

특이사항

  1. 함수 구문에는 매개변수 이름이 포함되어 있다.

TS가 익숙하지 않다면 읽기 어려울 듯 하다.

let first: (a: any, b: any) => any = (a, b) => a;

// 좀 더 정확하게 말하자면
let second: <T, U>(a: T, b: U) => U = (a, b) => b;
  1. 객체 리터럴 타입 구문이 객체 리터럴 값 구문과 꽤 유사하다.
let o: { n: number; xs: object[] } = { n: 1, xs: [] };


점진적 타이핑(Gradual typing)

TS는 표현식의 타입을 알 수 없을 때 any타입을 사용한다.

TS는 any를 제공할 때 에러가 발생되도록 하려면 tsconfig.json에서 "noImplictAny": true 또는 "strict": true를 설정하면 된다.

any 타입의 단점

  1. 타입에 의해 발생하는 오류를 잡아낼 수 없다.
const anys = [];
anys.push(1);
anys.push("oh");
anys.push({ anything: "hello" });

// anys안의 어떤 값도 함수가 아니기 때문에 동작하지 않는다.
anys.map(anys[1]); 
  1. any는 전염된다.
  • any 타입의 표현식과 함께 변수를 초기화하면, 변수 역시 any타입을 가진다.
let what = anys[0] + anys[1];


네임드 타입

네임드 타입은 타입에 이름을 붙히는 것이다. (변수 선언과 동일 시 된다.)

단, 타입 별칭은 재귀 정의와 타입 매개변수에 관련한 인터페이스에서는 다르게 동작한다.

type One = { p: string };
interface Two {
  p: string;
}
class Three {
  p = "Hello";
}

let x: One = { p: "hi" };
let two: Two = x;
two = new Three();

타입 매개변수(Type Parameters)

대부분의 C-계열 언어처럼, TS는 타입 매개변수의 선언을 요구한다.

function liftArray<T>(t: T): Array<T> {
  return [t];
}

대소문자 구분은 없지만 일반적으로 타입 매개 변수는 단일 대문자이다.
타입 매개 변수는 타입 클래스 제약과 비슷하게 동작하는 타입으로 제한 될 수 있다.

function firstish<T extends { length: number }>(t1: T, t2: T): T {
  return t1.length > t2.length ? t1 : t2;
}

TS는 일반적으로 인자 타입을 기반으로 호출할 때 타입 인자를 추론할 수 있기 때문에 대부분 타입 인자를 필요로 하지 않는다.
그 이유는 TS가 구조적이기 때문에 이름 기반의 시스템만큼 타입 매개 변수를 필요로 하지 않기 때문이다.

특히 함수를 다형성으로 만들 필요는 없다.
타입 매개변수는 매개변수를 같은 타입으로 제한하는 것처럼 타입 정보를 전파 하는데만 쓰여야 한다.

function length<T extends ArrayLike<unknown>>(t: T): number {}

function length(t: ArrayLike<unknown>): number {}

첫 번째 length에서 T는 필요하지 않다.
오직 한 번만 참조되며 다른 매개변수나 리턴 값을 제한하는데 사용되지 않기 때문이다.



상위 유형의 타입 (Higher-kinded types)

TS는 상위 유형의 타입이 없다.
때문에 아래와 같이 작성하는건 허용하지 않는다.

function length<T extends ArrayLike<unknown>, U>(m: T<U>) {}


포인트-프리 프로그래밍 (Point-free programming)

PFP는 (커링 및 함수 합성이 가능한)JS에서 가능하지만 장황하다.
TS에서는 PFP에 대한 타입 추론이 실패하는 경우가 많기 대문에 값 매개변수 대신 타입 매개변수르 지정하게 된다.
그 결과는 너무 장황하기 때문에 PFP는 피하는게 좋다.



readonlyconst

JS에서는 const 키워드를 사용하여 수정을 허용하지 않는 변수를 선언할 수 있는데 const 키워드로 선언된 변수의 값이 참조값이라면 여전히 수정이 가능하다.

const a = [1, 2, 3];
a.push(4);
a[0] = 5;

// a [5, 2, 3, 4]

TS는 추가적으로 프로퍼티에 readonly 제어자를 사용할 수 있다.

interface Rx {
  readonly x: number;
}
let rx: Rx = { x: 1 };

// 읽기 전용 속성이므로 'x'에 할당할 수 없습니다.ts(2540)
rx.x = 12;

매핑된 타입 Readonly<T>는 모든 프로퍼티를 readonly로 만든다.

interface X {
  x: number;
}
let rx: Readonly<X> = { x: 1 };

// 읽기 전용 속성이므로 'x'에 할당할 수 없습니다.ts(2540)
rx.x = 12; 

또한 부수효과를 발생시키는 메서드를 제거하고 배열 인덱스에 대한 변경을 방지하는 특정 ReadonlyArray<T>타입과, 해당 타입에 대한 특수 구문이 있다.

let a: ReadonlyArray<number> = [1, 2, 3];
let b: readonly number[] = [1, 2, 3];

// 'readonly number[]' 형식에 'push' 속성이 없습니다.ts(2339)
a.push(4); 

// 'readonly number[]' 형식의 인덱스 시그니처는 읽기만 허용됩니다.ts(2542)
b[0] = 5;

배열과 객체 리터럴에서 동작하는 const-assertion만 사용할 수 있다.

let a = [1, 2, 3] as const;

// 'readonly [1, 2, 3]' 형식에 'push' 속성이 없습니다.ts(2339)
a.push(4)

// 읽기 전용 속성이므로 '0'에 할당할 수 없습니다.ts(2540)
a[0] = 5;

그러나 위의 기능들은 기본적인 기능이 아니기 때문에 TS코드에 일관적으로 사용하지 않아도 된다.

profile
프론트엔드 개발자

0개의 댓글

관련 채용 정보