any 타입은 자바스크립트에 존재하는 모든 값을 오류 없이 받을 수 있다.
즉, 자바스크립트에서의 기본적인 사용 방식과 같으므로 타입을 명시하지 않는 것과 동일한 효과를 나타냄.
any 타입을 변수에 할당하는 것은 지양해야할 패턴
tsconfig.json noIm-plicitAny 옵션을 활성화하면 타입이 명시되지 않은 변수의 암묵적인 any 타입에 대한 경고를 발생시킬 수 있음.
any타입을 어쩔 수없이 사용해야할 대표적인 3가지
any 타입은 개발자에게 편의성과 확장성을 제공하기도 하지만 해당 값을 컨트롤하려면 파악해야 할 정보도 많다. 도구의 도움을 받을 수 없는 상태에서 온전히 개발자가 책임을 져야 한다.
unknown 타입은 any 타입과 유사하게 모든 타입의 값이 할당될 수 있다. 그러나 any를 제외한 다른 타입을 할당할 수 없다.
any
unknown
- 어떤 타입이든 unknown 타입에 할당 가능
- unknown 타입은 any 타입 외에 다른 타입으로 할당 불가능
// 할당하는 시점에는 에러가 발생하지 않음
const unknownFunction: unknown = () => console.log('this is unknown type")
//하지만 실행시에는 에러가 발생
unknownFunction();
함수뿐만 아니라 객체의 속성, 접근, 클래스 생성자 호출을 통한 인스턴스 생성 등 객체 내부에 접근하는 모든 시도에서 에러가 발생. 할당 시점에는 아무런 문제가 없는데 왜 호출 시 문제가 생기는지 의아할 수 있음. unknown 타입은 어떤 타입이 할당되었는지 알 수 없음을 나타내기 때문에 unknown 타입은 선언된 변수는 값을 가져오거나 내부 속성에 접근 x
any 타입을 특정 타입으로 수정해야 하는 것을 깜빡하고 누락하면 어떤 값이든 어떤 값이든 전달할 수 있기 때문에 런타임에 예상치 못한 버그가 발생할 가능성이 높다.
unknown 타입은 이러한 상황을 보완하기 위해 등장. any타입과 유사하지만 타입 검사를 강제하고 타입이 식별된 후에 사용 가능할 수 있기 때문에 any타입보다 더 안전.
function showModal(type:ModalType):void{
feedbackSlice.actions.createModal(type);
}
// 화살표 함수로 작성 시
const showModal = (type:ModalType):void =>{
feedbackSlice.actions.createModal(type);
}
자바스크립트에서는 함수에서 명시적인 반환문을 작성하지 않으면 기본적으로 undefined가 반환
하지만 타입스크립트에서는 void 타입이 사용되는데 이것은 undefined가 아니다.
타입스크립트에서 함수가 어떤 값을 반환하지 않는 경우에는 void를 지정하여 사용한다고 생각하면 됨.
또한 명시적인 의미를 부여하는 관점에서 undefined와 null 타입 키워드를 직접 사용해서 타입을 지정하는 것이 바람직하다.
never 타입도 일반적으로 함수와 관련하여 많이 사용되는 타입.
never라는 단어가 내포하고 있는 의미처럼 never 타입은 값을 반환할 수 없는 타입을 말함.
여기서 값을 반환하지 않는것과 반환할 수 없는 것을 명확히 구분.
자바스크립트에서 값을 반환할 수 없는 예는 크게 2가지
자바스크립트에서는 런타임에 의도적으로 에러를 발생시키고 캐치할 수 있다. throw 키워드를 사용하면 에러를 발생시킬 수 있는데, 이는 값을 반환하는 것으로 간주하지 않는다.
드물지만 함수 내에서 무한 루프를 실행하는 경우가 있을 수 있다. 무한 루프는 결국 함수가 종료되지 않음을 의미하기 때문에 값을 반환하지 못한다.
never 타입은 모든 타입의 하위 타입, 즉 never 자신을 제외한 어떤 타입도 never타입에 할당될 수 없다. any타입 조차도 할당할 수 없다.
배열 타입을 가리키는 Array 키워드는 자바스크립트에서도 Object.prototype.toString.call 연산자를 사용하여 확인 가능. typeof를 사용하여 타입을 알수도 있지만 이 함수를 사용하는 이유 typeof의 경우 객체 타입을 단순히 object 타입으로 알려주지만 Object.prototype.toString.call 함수는 객체의 인스턴스까지 알려주기 때문
타입스크립트에서 다시 Array를 언급하는 이유
리액트는 16.8 버전부터 도입된 hook이라는 요소 중 useState는 튜플 타입을 반환
첫번째 원소는 훅으로부터 생성 및 관리되는 상태 값을 의미
두번째 원소는 해당 상태를 조작할 수 있는 세터를 의미한다.
또한 옵셔널 프로퍼티를 명시하고 싶다면 물음표(?) 기호와 함께 해당 속성을 선언
해당 원소는 옵셔널하기 때문에 해당 인덱스에 필수적으로 자리 잡고 있지 않을 수 있음을 선언
💡 옵셔널 특정 속성 또는 매개변수가 값이 있을 수도 있고 없을 수도 있는 것을 의미한다. 즉 선택적 매개변수 또는 선택적 속성은 필수적으로 존재하지 않아도 되며 선택적으로 사용될 수 있음을 나타낸다.enum 타입은 열거형이라고도 부르는데 타입스크립트에서 지원하는 특수한 타입.
enum은 일종의 구조체를 만드는 타입 시스템이다. 자바스크립트 객체의 모양새와 닮았다.
다만 타입스크립트는 명명한 각 멤버의 값을 스스로 추론한다. 기본적인 추론 방식은 숫자 0부터 1씩 늘려가며 값을 할당.
enum ProgorammingLanguage {
Typescript, //0
Javascript, //1
Java// 2
}
//각 멤버에게 접근하는 방식은 자바스크립트에서 객체의 속성에 접근하는 방식과 동일
ProgorammingLanguage.Typescirpt //0
ProgorammingLanguage.Java //2
ProgorammingLanguage["Javascript] //1
//또한 역방향으로 접근 가능하다.
ProgorammingLanguage[2] //"Java"
enum ProgorammingLanguage {
Typescript = 300
Javascript, //301
Java// 302
}
enum 타입은 주로 문자열 상수를 생성하는 데 사용 응집력있는 집합 구조체를 만들 수 있으며, 사용자 입장에서도 간편하게 활용할 수 있다.
enum ItemType{
HELLO='안녕'
BYE = '잘가'
}
const checkItem = (itemStatus:ItemType) =>{
switch(itemStatus){
case ItemType.HELLO
case ItemType.BYE
return false;
default:
return true;
}
}
checkItem 함수의 인자인 itemStatus는 ItemType 열거형 타입을 가진다.
이를 통해 얻을 수 있는 효과.
주의할 점
숫자로만 이루어져 있거나 타입스크립트가 자동으로 추론한 열거형은 안전하지 않을 수 있다.
교차 타입을 사용하면 여러 가지 타입을 결합하여 하나의 단일 타입으로 만들 수 있다.
기존에 존재하는 다른 타입들을 합쳐서 해당 타입의 모든 멤버를 가지는 새로운 타입을 생성
교차 타입은 &을 사용해서 표기한다.
타입 C가 타입 A와 B의 교차 타입 즉, A & B라면 타입 C는 타입 A와 타입 B의 모든 멤버를 가지고 있는 타입.
교차 타입이 타입 A와 타입 B를 모두 만족하는 경우라면, 유니온 타입은 타입 A 또는 타입 B 중 하나가 될 수 있는 타입을 말하며 A | B같이 표기한다. 주로 특정 변수가 가질 수 있는 타입을 전부 나열하는 용도로 사용된다. 교차 타입과 마찬가지로 2개 이상의 타입을 이어 붙일 수 있고 타입 별칭을 중복을 줄일 수 도 있다.
인덱스 시그니처는 특정 타입의 속성 이름을 알 수 없지만 속성값의 타입을 알고 있을 때 사용하는 문법이다. 인터페이스 내부에 [key:K]:T 꼴로 타입을 명시해주면 되는데 이는 해당 타입의 속성 키는 모두 K 타입이어야 하고 속성값은 모두 T 타입을 가져야 한다는 의미.
interface IndexSignatureEx{
[key:string]:number;
}
인덱스 시그니처를 선언할 때 다른 속성을 추가로 명시해줄 수 있는데 이때 추가로 명시된 속성은 인덱스 시그니처에 포함되는 타입이어야 함.
인덱스드 엑세스 타입은 다른 타입의 속성이 가지는 타입을 조회하기 위해 사용
첫번째 예시는 Example 타입의 a 속성이 가지는 타입을 조회하기 위한 인덱스드 엑세스 타입이다.
인덱스에 사용되는 타입 또한 그 자체로 타입이기 때문에 유니온 타입, keyof, 타입 별칭 등의 표현을 사용할 수 있다.
type Example={
a:number;
b:string;
c:boolean;
}
type IndexedAccess = Example["a"];
type IndexedAccess2 = Example["a" | "b]; // number | string
type IndexedAccess3 = Example[keyof Example]; // number | string | boolean
type Exalias = "b" | "c";
type IndexedAccess4 = Example[Exalias] //string | boolean
보통 map이라고 하면 유사 행태를 가진 여러 항목의 목록 A를 변환된 항목의 목록 B로 바꾸는 것을 의미한다. 자바스크립트의 map 메서드를 생각하면 이해하기 쉽다. 맵드 타입은 다른 타입을 기반으로 한 타입을 선언할 때 사용하는 문법인데, 인덱스 시그니처 문법을 사용해서 반복적인 타입 선언을 효과적으로 줄일 수 있다. readonly는 읽기 전용으로 만들고 싶을 때 붙여주는 수식어이고, ?는 선택적 매개변수로 만들고 싶을 때 붙여주는 수식어이다. 맵드 타입의 특이점은 이러한 수식어를 더해주는 것뿐만 아니라 제거할 수도 있다는 것. 기존 타입에 존재하던 readonly나 ? 앞에 -를 붙여주면 해당 수식어를 제거한 타입을 선언할 수 있다.
템플릿 리터럴 타입은 자바스크립트의 템플릿 리터럴 문자열을 사용하여 문자열 리터럴 타입을 ㅅ건언할 수 있는 문법.
type Stage =
| "init"
| "select-image"
| "edit-image"
| "decorate-card"
| "capture=image"
type StageName = `${Stage}-stage`;
// 'init-stage' | 'select-image-stage' | ...
Stage 타입의 모든 유니온 멤버 뒤에 -stage를 붙여서 새로운 유니온 타입을 만들었다.
제네릭은 C나 자바 같은 정적 언어에서 다양한 타입 간에 재사용성을 높이기 위해 하는 문법,
제네릭의 사전적 의미를 찾아보면 특징이 없거나 일반적인 것을 뜻한다.
타입스크립트 제네릭의 개념을 풀어보면 함수, 타입, 클래스 등에서 내부적으로 사용할 타입을 미리 정해두지 않고 타입 변수를 사용해서 해당 위치를 비워 둔 다음에, 실제로 그 값을 사용할 때 외부에서 타입 변수 자리에 타입을 지정하여 사용하는 방식을 말한다.
타입 변수는 일반적으로 와 같이 꺾쇠괄호 내부에 정의되며, 사용할 때는 함수에 매개변수를 넣는 것과 유사하게 원하는 타입을 넣어주면 됨.
type ExampleArrayType<T> = T[];
const array1: ExampleArrayType<string> = ["치킨","피자","우동"]
제네릭을 사용하면 배열요소가 전부 동일한 타입을 보장할 수 있다.
참고로 제네릭 함수를 호출할 때 반드시 꺾쇠괄호 안에 타입을 명시해야 하는 것은 아니다.
타입을 명시하는 부분을 생략하면 컴파일러가 인수를 보고 타입을 추론해준다.
제네릭은 일반화된 데이터 타입을 의미한다. 따라서 함수나 클래스등의 내부에서 제네릭을 사용할 때 어떤 타입이든 될 수 있다는 개념을 알고 있어야 한다.
제네릭을 사용할 때 주의해야할 점. 파일 확장자가 tsx일 때 화살표 함수에 제네릭을 사용하면 에러가 발생
이러한 상황을 피하기 위해서는 제네릭 부분에 extends 키워드를 사용하여 컴파일러에게 특정 타입의 하위 타입만 올 수 있음을 확실히 알려주면 된다.
어떤 함수의 매개변수나 반환 값에 다양한 타입을 넣고 싶을 때 제네릭을 사용할 수 있다.
호출 시그니처는 타입스크립트의 함수 타입 문법으로 함수의 매개변수와 반환 타입을 미리 선언하는 것을 말한다.
제네릭 클래스는 외부에서 입력된 타입을 클래스 내부에 적용할 수 있는 클래스이다.
클래스 이름 뒤에 타입 매개변수인 를 선언해준다. 는 메서드의 매개변수나 반환 타입으로 사용될 수 있다.
제네릭 클래스를 사용하면 클래스 전체에 걸쳐 타입 매개변수가 적용된다. 특정 메서드만을 대상으로 제네릭을 적용하려면 해당 메서드를 제네릭 메서드로 선언하면 된다.
타입스크립트에서 제한된 제네릭은 타입 매개변수에 대한 제약 조건을 설정하는 기능을 말한다.
string 타입으로 제약하려면 타입 매개변수는 특정 타입을 상속(extends)해야한다.
type ErrorRecord<Key extends string> = Exclude<Key,ErrorCodeType> extends never
? Partial<Record<Key,boolean>>
: never;
이처럼 타입 매개변수가 특정 타입으로 묶였을 때 키를 바운드 타입 매개변수라고 부른다.
그리고 string을 키의 상한 한계라고 한다.
제네릭 타입은 여러 타입을 상속받을 수 있으며 타입 매개변수를 여러 개 둘 수도 있다.
<Key extends string>
타입을 이런 식으로 제약해버리면 제네릭의 유연성을 잃어버린다. 제네릭의 유연성을 잃지 않으면서 타입을 제약해야 할 떄는 타입 매개변수나 유니온 타입을 상속해서 선언하면 된다.
<Key extends string | number>
유니온 타입으로 T가 여러 타입을 받게 할 수는 있지만, 타입 매개변수가 여러 개일 때는 처리할 수 없다.
제네릭의 장점은 다양한 타입을 받게 함으로써 코드를 효율적으로 재사용할 수 있는 것이다.
바로 API 응답 값의 타입을 지정할 때이다.
export interface MobileApiResponse<Data>{
data:Data;
statusCode:string;
statusMessage?:string;
}
export const fetchPriceInfo = ():Promise<MobileApiResponse<PriceInfo>>=>{
const priceUrl = "" //url 주소
return request({
method:"GET",
url:priceUrl
})
}
하지만 굳이 필요하지 않은 곳에 제네릭을 사용하면 오히려 독이 되어 코드를 복잡하게 만든다.
제네릭은 코드의 재사용성을 높이고 타입 추론을 하는 데 사용된다. 그러나 any를 사용하면 제네릭의 장점과 타입 추론 및 타입 검사를 할 수 있는 이점을 누릴 수 없게 된다.