앞선 글에 설명하지 않았던 TS타입에 대해 더 깊게 적어본다.
타입 | 설명 |
---|---|
unknown | 최상위 타입 |
never | 하위 타입 |
객체 리터럴 | { property: Type } 등등 |
void | 리턴 타입으로 사용하기 위해 의도된 undefined 의 서브타입 |
T[] | 수정가능한 배열들, Array<T> 로도 사용 가능 |
[T, T] | 고정된 길이지만 수정 가능한 튜플 |
(t: T) => U | 함수 |
TS가 익숙하지 않다면 읽기 어려울 듯 하다.
let first: (a: any, b: any) => any = (a, b) => a;
// 좀 더 정확하게 말하자면
let second: <T, U>(a: T, b: U) => U = (a, b) => b;
let o: { n: number; xs: object[] } = { n: 1, xs: [] };
TS는 표현식의 타입을 알 수 없을 때 any
타입을 사용한다.
TS는
any
를 제공할 때 에러가 발생되도록 하려면tsconfig.json
에서"noImplictAny": true
또는"strict": true
를 설정하면 된다.
any
타입의 단점const anys = [];
anys.push(1);
anys.push("oh");
anys.push({ anything: "hello" });
// anys안의 어떤 값도 함수가 아니기 때문에 동작하지 않는다.
anys.map(anys[1]);
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();
대부분의 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는 필요하지 않다.
오직 한 번만 참조되며 다른 매개변수나 리턴 값을 제한하는데 사용되지 않기 때문이다.
TS는 상위 유형의 타입이 없다.
때문에 아래와 같이 작성하는건 허용하지 않는다.
function length<T extends ArrayLike<unknown>, U>(m: T<U>) {}
PFP는 (커링 및 함수 합성이 가능한)JS에서 가능하지만 장황하다.
TS에서는 PFP에 대한 타입 추론이 실패하는 경우가 많기 대문에 값 매개변수 대신 타입 매개변수르 지정하게 된다.
그 결과는 너무 장황하기 때문에 PFP는 피하는게 좋다.
readonly
와 const
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코드에 일관적으로 사용하지 않아도 된다.