타입별칭
타입 별칭을 이용하면 다음과 같이 변수를 선언하듯 타입을 별도로 정의할 수 있습니다.
type 타입_이름 = 타입 형태로 타입을 정의합니다.
//타입별칭
type User = {
id: number;
name: string;
nickname: string;
birth: string;
}; // 같은 스코프 내에서는 중복정의 X
function func(){
type User = {
// 이 스코프 내에서는 User를 이런식으로 정의, 밖에서는 저런식으로 정의
}
}
let user: User ={
id: 1,
name: "정지응",
nickname:"Becky",
birth: "1999.06.30",
}
let user2: User ={
id:2,
name:"이동웅",
nickname: "이동곰",
birth: "2001.02.03",
}
참고로 동일한 스코프에 동일한 이름의 타입 별칭을 선언하는 것은 불가능합니다. 마치 변수 선언과 유사합니다.
그러나 스코프가 다르다면 다음과 같이 중복된 이름으로 여러개의 별칭을 선언해도 상관 없습니다.
이전 시간에 타입 관련 문법은 컴파일과 함께 모두 사라진다고 살펴보았습니다. 타입 별칭 또한 타입 관련 문법이기 때문에 컴파일 결과 사라집니다. 다음은 chapter4.ts를 tsc로 컴파일 한 결과입니다.
let user = {
id: 1,
name: "이정환",
nickname: "winterlood",
birth: "1997.01.07",
bio: "안녕하세요",
location: "부천시",
};
let user2 = {
id: 2,
name: "홍길동",
nickname: "winterlood",
birth: "1997.01.07",
bio: "안녕하세요",
location: "부천시",
};
export {};
인덱스 시그니처
인덱스 시그니쳐는 객체 타입을 유연하게 정의할 수 있도록 돕는 특수한 문법입니다.
만약 이때 countryCodes에 100개의 프로퍼티(국가 코드)가 추가 되어야 한다면 타입 정의에도 각 프로퍼티를 모두 정의해주어야 하기 때문에 매우 불편할 것 입니다.
type CountryCodes = {
Korea: string;
UnitedState: string;
UnitedKingdom: string;
// (... 약 100개의 국가)
Brazil : string
};
let countryCodes: CountryCodes = {
Korea: "ko",
UnitedState: "us",
UnitedKingdom: "uk",
// (... 약 100개의 국가)
Brazil : 'bz'
};
바로 이럴때 인덱스 시그니쳐를 이용하면 다음과 같이 간단하게 타입을 정의할 수 있습니다.
type CountryCodes = {
[key: string]: string;
};
let countryCodes: CountryCodes = {
Korea: "ko",
UnitedState: "us",
UnitedKingdom: "uk",
// (... 약 100개의 국가)
Brazil : 'bz'
};
[key : string] : string 은 인덱스 시그니쳐 문법으로 이 객체 타입에는 key가 string 타입이고 value가 string 타입인 모든 프로퍼티를 포함된다 라는 의미입니다.
한가지 주의할 점은 인덱스 시그니쳐를 사용하면서 동시에 추가적인 프로퍼티를 또 정의할 때에는 인덱스 시그니쳐의 value 타입과 직접 추가한 프로퍼티의 value 타입이 호환되거나 일치해야 합니다.
따라서 다음과 같이 서로 호환되지 않는 타입으로 설정하면 오류가 발생합니다.
type CountryNumberCodes = {
[key: string]: number;
Korea: string; // 오류!
};
열거형(Enum) 타입
enum Role {
ADMIN,
USER,
GUEST,
}
3개의 멤버 ADMIN, USER, GUEST를 나열한 열거형 타입을 하나 만들었습니다. 설명 편의상 앞으로 열거형 타입을 enum이라 통칭합니다.
enum Role {
ADMIN = 0,
USER = 1,
GUEST = 2,
}
enum의 각 멤버에는 다음과 같이 숫자를 할당할 수 있습니다.
// enum 타입
// 여러가지 값들에 각각 이름을 부여해 열거해두고 사용하는 타입
enum Role {
ADMIN = 0, //// 숫자를 할당하지 않아도, 자동으로 0,1,2 가 들어감
USER = 1,
GUEST = 2,
}
const user1 = {
name: "이정환",
role: Role.ADMIN, //관리자
};
const user2 = {
name: "홍길동",
role: Role.USER, // 회원
};
const user3 = {
name: "아무개",
role: Role.GUEST, // 게스트
};
console.log(user1, user2, user3)
// { name: '이정환', role: 0 } { name: '홍길동', role: 1 } { name: '정지은', role: 2 }
- Enum 쓰는 이유는 같은 프로퍼티에 실수로 'ko'라고 적어야 할것을 오타가 발생해 "Ko" 라고 적는 문제를 막을 수 있음
값을 헷갈리지 않게 넣기 위함!
enum은 컴파일 결과 객체가 된다.
enum은 컴파일될 때 다른 타입들 처럼 사라지지 않고 자바스크립트 객체로 변환됩니다.
따라서 우리가 위에서 했던 것 처럼 값으로 사용할 수 있는 것 입니다.
any 타입
any 타입은 타입스크립트에서만 제공되는 특별한 타입으로 타입 검사를 받지 않는 특수한 치트키 타입입니다.
다음과 같이 아주 범용적으로 사용되어야 하는 변수가 하나 있다고 가정합니다.
//any(어떤 타입이던지)
//특정 변수의 타입을 모를 때
let anyVar: any = 10;
anyVar = "hello";
anyVar = true;
anyVar = {};
anyVar =() =>{};
//메서드도 제약없이 사용(자바스크립트 같이)
let num: number = 10;
num = anyVar;// number 타입에 any타입을 집어넣을 수 있음
// anyVar.toUppeCase 같은 경우, 런타임에 오류가 날 수 있으니 any는 최대한 자제
any는 최대한 사용하지 마세요
let anyVar: any = 10;
anyVar = "hello";
anyVar = true;
anyVar = {};
anyVar.toUpperCase(); //오류발생!
anyVar.toFixed();
anyVar.a;
우리가 아까 작성한 다음과 같은 코드를 컴파일 하거나 ts-node로 실행해보면 런타임 오류가 발생합니다.
이러면 사실 타입스크립트를 사용하는 이유가 없습니다.
unknown 타입
unknown 타입은 any 타입과 비슷하지만 보다 안전한 타입입니다.
unknown 타입의 변수는 다음과 같이 어떤 타입의 값이든 다 저장할 수 있습니다. (언노운에 가능)
let unknownVar: unknown;
unknownVar = "";
unknownVar = 1;
unknownVar = () => {};
그러나 반대로는 안됩니다. unknown 타입의 값은 어떤 타입의 변수에도 저장할 수 없습니다. (언노운이 불가능)
let num: number = 10;
(...)
let unknownVar: unknown;
unknownVar = "";
unknownVar = 1;
unknownVar = () => {};
num = unknownVar; // 오류 !
또 unknown 타입의 값은 어떤 연산에도 참여할 수 없으며, 어떤 메서드도 사용할 수 없습니다.
정리하자면 unknown 타입은 독특하게도 변수의 타입으로 정의되면 모든 값을 할당받을 수 있게 되지만, 반대로 unknown 타입의 값은 그 어떤 타입의 변수에도 할당할 수 없고, 모든 연산에 참가할 수 없게 됩니다. 쉽게 정리하면 오직 값을 저장하는 행위밖에 할 수 없게 됩니다.
//타입정제 : 타입좁히기 과정을 통해서만 다른 타입에 unknownVar 타입을 갖다 붙일 수 있음
if(typeof unknownVar === "number"){
num = unknownVar;
}
참고로 타입스크립트에서는 위 코드처럼 조건문을 이용해 특정 값이 특정 타입임을 보장할 수 있게 되면 해당 값의 타입이 자동으로 바뀝니다. 이를 타입 좁히기라고 하며 3섹션에서 자세히 다룹니다.
따라서 특정 변수가 당장 어떤 값을 받게 될 지 모른다면 any 타입으로 정의하는 것 보단 unknown 타입을 이용하는게 훨씬 안전한 선택이 됩니다.
void 타입
void 타입은 아무런 값도 없음을 의미하는 타입입니다.
보통은 다음과 같이 아무런 값도 반환하지 않는 함수의 반환값 타입을 정의할 때 사용합니다.
//void (공허)
//void => 아무것도 없음
//반환
function func1():string {
return "hello";
}
//출력
//왜 void씀 ? => null이나 undefined는 마지막에 각자 리턴을 해줘야 하는데, void는 리턴안하고 출력만 해도 ok
function func2():void {
console.log("hello")
}
let a:void;
//a = 1; //'number' 형식은 'void' 형식에 할당할 수 없습니다.
//a ='hello'; // 'string' 형식은 'void' 형식에 할당할 수 없습니다.
//a = {}; // '{}' 형식은 'void' 형식에 할당할 수 없습니다.
// void 타입은오직, undefined만을 담을 수 있다
a = undefined
//예외적으로 tsconfig.json 에서 "strictNullChecks": false 로 설정하면 void 타입에 null을 넣을 수 있다
그러나 void 타입의 변수에는 undefiend 이외의 다른 타입의 값은 담을 수 없습니다.
그 이유는 void 타입이 undefiend 타입을 포함하는 타입이기 때문인데 이에 대해서는 3섹션에서 자세히 살펴볼 예정입니다.
let a: void;
a = undefined;
그런데 만약 이때 tsconfig.json에 엄격한 null 검사(strictNullChecks) 옵션을 해제(False)로 설정하면 특별히 이때에는 void 타입의 변수에 null 값도 담을 수 있게 됩니다.
// "strictNullChecks: false" 일 경우
let a: void;
a = undefined;
a = null;
- 자바스크립트에는 이미 아무런 값도 없음을 의미하는 null과 undefined이 존재하는데 왜 void 타입이 추가로 필요한 걸까요?
=> 반환문이 없는 함수의 경우, void 타입을 이용해야 함!
위와 같이 반환값 타입을 undefined으로 설정하면 함수가 진짜 undefined 을 반환해야 합니다.
function func2(): undefined {
console.log("hello");
return undefined;
}
null 타입으로 정의해도 동일합니다. 진짜 null 값을 반환해야 합니다.
function func2(): null {
console.log("hello");
return null;
}
never 타입
never 타입은 불가능을 의미하는 타입입니다.
보통 다음과 같이 함수가 어떠한 값도 반환할 수 없는 상황일 때 해당 함수의 반환값 타입을 정의할 때 사용됩니다.
function func3(): never {
while (true) {}
}
함수 func3는 무한 루프를 돌기 때문에 아무런 값도 반환할 수 없습니다. 엄밀히 말하면 이 함수는 영원히 종료될 수 없기 때문에 뭔가를 반환한다는 것 자체가 '불가능' 합니다
이렇게 불가능 한 값의 타입을 정의할 때 never 타입을 사용합니다.
무한 루프 외에도 다음과 같이 의도적으로 오류를 발생시키는 함수도 never 타입으로 반환값 타입을 정의할 수 있습니다
function func4(): never {
throw new Error();
}
변수의 타입을 never로 정의하면 any를 포함해 그 어떠한 타입의 값도 이 변수에 담을 수 없게 됩니다.
//func4는 실행되면 바로 중지되는 함수 => 모순된 함수 :never
function func4(): never{
throw new Error();
}
let b: never;
let anyVar: any;
//정말 아무런 타입도 never 타입 변수에 담을 수 없음
b = 1; //'number' 형식은 'never' 형식에 할당할 수 없습니다.
b = {}; //'{}' 형식은 'never' 형식에 할당할 수 없습니다.
b= ""; //'string' 형식은 'never' 형식에 할당할 수 없습니다.
b= undefined; //'undefined' 형식은 'never' 형식에 할당할 수 없습니다.
b= null; //'null' 형식은 'never' 형식에 할당할 수 없습니다.
b= anyVar; //'any' 형식은 'never' 형식에 할당할 수 없습니다.
'typescript' 카테고리의 다른 글
속성이 다른 데이터들을 같은 구성으로 렌더링하기 (4) | 2023.07.23 |
---|---|
함수와 타입 (2) | 2023.06.01 |
타입스크립트 이해하기 (0) | 2023.05.31 |
타입스크립트 기본(원시타입, 비원시타입, 리터럴 타입) (0) | 2023.05.31 |
타입스크립트의 점진적 타입시스템 (0) | 2023.05.31 |