본문 바로가기

Frontend/Typescript

타입스크립트 기본

타입스크립트의 기본 타입은 다음과 같다.

 

원시 타입과 리터럴 타입

// number
let num1: number = 123;
let num2: number = -123;
let num3: number = 0.123;
let num4: number = -0.123;
let num5: number = Infinity;
let num6: number = -Infinity;
let num7: number = NaN;

num1.toFixed();

// string
let str1: string = 'hello';
let str2: string = "hello";
let str3: string = `hello`;
let str4: string = `hello ${num1}`;

str1.toLowerCase();

// boolean
let bool1: boolean = true;
let bool2: boolean = false;

// null
let null1: null = null;

// undefined
let unde1: undefined = undefined;

원시 타입은 하나의 값만 저장하는 타입이다.

사실 자바스크립트에서 배운 내용이라 깊게 설명하지 않고 지나가도 될 것 같다.

 

let numA: number = null;

원래는 number 타입의 값에 null을 배정하면 에러가 발생하지만, 컴파일러 옵션에서 strictNullChecks 옵션을 false로 해주면 가능하다.

이 옵션은 null 타입이 아닌 것에 null 값을 할당하는 것을 허용할지 말지에 대한 옵션이다.

또한, strict 옵션은 strictNullChecks 옵션의 상위 옵션이기 때문에 strict 옵션이 켜져 있으면 strictNullChecks 옵션도 켜져 있다.

strict 옵션을 true로, strictNullChecks 옵션을 false로 지정할 수도 있다.

 

타입스크립트에는 리터럴 타입이라는 것이 존재하는데, 아래 코드처럼 타입 자체를 값으로 지정할 수 있다는 것이다.

// 리터럴 타입
// 리터럴 -> 값
let numA: 10 = 10;
numA = 12;  // '12' 형식은 '10' 형식에 할당할 수 없습니다.

let strA: 'hello' = 'hello';

let boolA: true = true;

 

배열과 튜플

// 배열
let numArr: number[] = [1, 2, 3];
let strArr: string[] = ['hello', 'hi'];
let boolArr: Array<boolean> = [true, false, true];

// 배열에 들어가는 요소들의 타입이 다양할 경우
let multiArr: (number | string)[] = [1, 'hello'];

// 다차원 배열의 타입을 정의하는 방법
let doubleArr: number[][] = [
    [1, 2, 3],
    [4, 5]
];

// 튜플
// 길이와 타입이 고정된 배열
let tup1: [number, number] = [1, 2];
let tup2: [number, string, boolean] = [1, '2', true];

tup1.push(1);
tup1.pop();
tup1.pop();
tup1.pop();

const users: [string, number][] = [
    ['이정환', 1],
    ['홍서현', 2],
]

튜플 타입타입스크립트에만 있는 타입이라, 자바스크립트로 컴파일하게 되면 그냥 배열로 표현된다.

그래서 배열 메서드인 push, pop을 사용해도, 오류를 보여주지 않는다.

튜플 타입 배열 메서드를 사용할 때 주의해서 사용해야 한다.

튜플은 인덱스에 따른 값 위치가 정해져 있고, 그 순서가 중요한 경우 사용한다.

 

객체

// object
let user1: object = {
    id: 1,
    name: '이정환'
}

user.id; // 'object' 형식에 'id' 속성이 없습니다.

// 객체 리터럴 타입 사용
let user2: {
    id: number,
    name: string
} = {
    id: 2,
    name: '홍서현'
};

let user3: {
    name: string;
    id?: number;  // 선택적 프로퍼티
} = {
    name: '홍길동'
}

let config: {
    readonly apiKey: string;
} = {
    apiKey: 'API KEY'
}

config.apiKey = 'hacked';  // 읽기 전용 속성이므로 'apiKey'에 할당할 수 없습니다.

타입스크립트에서 object라는 타입은 해당 값이 객체라는 것 이외에는 아무런 정보도 없는 타입이라 해당 객체의 프로퍼티에 접근하면 위 코드처럼 에러가 발생한다.

그래서 이럴 때는 객체 리터럴 타입을 사용해야 한다.

 

타입스크립트구조(프로퍼티)를 기준으로 타입을 정의하는데, 이를 구조적 타입 시스템(프로퍼티 기반 타입 시스템)이라고 부른다.

구조적 타입 시스템의 반대는 이름을 기준으로 타입을 정의하는 명목적 타입 시스템이다.

 

한편, 타입 정의 시 readonly를 붙여주게 되면, 해당 값을 읽기 전용으로 변경하는 것이다.

이후, 해당 값을 변경하려고 하면 에러가 난다.

 

타입 별칭과 인덱스 시그니처

// 타입 별칭
type User = {
    id: number;
    name: string;
    nickname: string;
    birth: string;
    bio: string;
    location: string;
};

let user1: User = {
    id: 1,
    name: '이정환',
    nickname: 'winterlood',
    birth: '1997.01.07',
    bio: '안녕하세요',
    location: '부천시'
};

// 인덱스 시그니처
type CountryCodes = {
    [key: string]: string;
};

let countryCodes: CountryCodes = {
    Korea: 'ko',
    UnitedStates: 'us',
    UnitedKingdom: 'uk'
};

let countryCodes2 = {};

type CountryNumberCodes = {
    [key: string]: number;
    Korea: number;
}

let countryNumberCodes = {
    Korea: 410,
}

인덱스 시그니처는 해당 타입에 정의된 것만 위반하지 않으면 모든 객체를 허용해서 빈 객체도 에러가 발생하지 않는다.

만약 필수적인 프로퍼티가 있다면, 인덱스 시그니처 아래에 해당 프로퍼티에 대한 타입을 지정해주면 된다.

 

하지만, CountryNumberCodes 타입에 Korea 프로퍼티의 value 타입을 number가 아닌 string으로 지정해주게 되면 위의 인덱스 시그니처의 타입과 호환이 되지 않기 때문에 에러가 발생한다.

 

Enum 타입

// enum 타입
// 여러가지 값들의 각각 이름을 부여해 열거해두고 사용하는 타입
enum Role {
    ADMIN,
    USER,
    GUEST
};

enum Language {
    korean = 'ko',
    english = 'en'
};

const user1 = {
    name: '이정환',
    role: Role.ADMIN,  // 0
    language: Language.korean
};

const user2 = {
    name: '홍길동',
    role: Role.USER,  // 1
    language: Language.english
};

const user3 = {
    name: '홍서현',
    role: Role.GUEST,  // 2
    language: Language.english
}

Role 처럼 이넘의 값들에 숫자가 할당되는 것을 숫자형 이넘이라고 한다.

문자형 이넘도 있는데, Language 같은 것을 문자형 이넘이라고 한다.

 

이넘은 자바스크립트로 컴파일 시 객체로 변환된다.

 

Any와 Unknown 타입

// any
// 특정 변수의 타입을 우리가 확실히 모를 때
let anyVar: any = 10;
anyVar = 'hello';

anyVar.toUpperCase();
anyVar.toFixed();  // TypeError가 런타임에 발생

let num: number = 10;
num = anyVar;

// unknown
let unknownVar: unknown;
unknownVar = '';
unknownVar = 1;
unknownVar = () => { };

num = unknownVar;  // 'unknown' 형식은 'number' 형식에 할당할 수 없습니다.
unknownVar.toUpperCase();  // 'unknownVar'는 'unknown' 형식입니다.

if (typeof unknownVar === 'number') {
    num = unknownVar;
}

any 타입은 모든 타입의 값을 다 할당받을 수 있고, 모든 타입의 변수에 any 타입의 값을 집어넣을 수도 있다.

any 타입을 사용하면 타입 검사 시 타입을 검사하지 않는 것과 같아 런타임에 에러가 발생할 수 있다.

따라서 any 타입은 최대한 사용하지 않는 것이 좋다.

 

unknown 타입은 any와 유사하지만, 변수에 unknown 타입의 값을 집어넣을 수는 없다.

그래서 변수의 값 타입이 아직 확실하지 않을 때는 any 타입보다는 unknown 타입을 쓰는 것이 좋다.

 

Void와 Never 타입

// void
// void -> 아무것도 없음을 의미하는 타입

function func1(): string {
    return 'hello';
}

function func2(): void {
    console.log('hello');
}

let a: void;
a = 1;  // 'number' 형식은 'void' 형식에 할당할 수 없습니다.
a = undefined;

// never
// never -> 존재하지 않는
// 불가능한 타입
function func3(): never {
    while (true) { }  // 무한 루프
}

function func4(): never {
    throw new Error();
}

let anyVar: any;

let a: never;
a = 1;  // 'number' 형식은 'never' 형식에 할당할 수 없습니다.
a = undefined;  // 'undefined' 형식은 'never' 형식에 할당할 수 없습니다.
a = anyVar;  // 'any' 형식은 'never' 형식에 할당할 수 없습니다.

변수에도 void 타입을 지정해줄 수 있는데, 이 경우, 해당 변수에 undefined를 제외 어떠한 값도 담을 수 없다.

strictNullChecks 옵션을 false로 두게 되면 예외적으로 void 타입의 변수에도 null을 넣을 수 있게 된다.

 

never 타입 변수에는 어떠한 값도 넣을 수 없다.

never 타입 변수에는 strictNullChecks 옵션을 false로 둬도 null을 넣을 수가 없다.

'Frontend > Typescript' 카테고리의 다른 글

제네릭  (0) 2023.06.20
인터페이스와 클래스  (0) 2023.06.20
함수와 타입  (0) 2023.06.20
타입스크립트 이해하기  (0) 2023.06.20
타입스크립트 개론  (0) 2023.06.19