Front-end/TypeScript

TypeScript 기본(2)

kwon-jin2-development 2024. 10. 15. 16:28

1. any 타입

any타입은 TS에서 모든 타입의 값을 허용하는 타입이다. 즉, any를 사용하면 그 변수에 어떤 값이든 저장할 수 있다.

타입 체크를 하지 않기 때문에 JS처럼 유연한 코드가 작성될 수 있지만, TS의 주요 장점인  타입 안정성을 희생한다.

let anything: any;

anything = 'Hello';  // 문자열 할당
anything = 42;       // 숫자 할당
anything = true;     // 불리언 할당
anything = [1, 2, 3]; // 배열 할당
anything = { name: 'Lois' };  // 객체 할당

any타입의 변수에는 어떤 값이든 할당할 수 있다. any는 타입 체크를 하지 않기 때문에 TS의 타입 안정성을 무시한다.

any를 사용할 때는 다음과 같은 경우에 사용하는 것이 좋다.

  • 기존 JS 코드와 호환할 때: TS로 마이그레이션 중인 JS 프로젝트에서, 일부분의 타입을 추론할 수 없을 때 임시로 사용할 수 있다.
  • 알 수 없는 값을 다룰 때: 외부 API에서 변환된 데이터의 타입이 명확하지 않은 경우 사용할 수 있다.

any와 유사한 역할을 하는 타입으로 unknown이 있다. any는 타입체크를 완전히 무시하는 반면, unknown은 값을 사용할 때 타입을 확인해야 한다.

let value: unknown = 'hello';

// 에러발생: 'unknown'은 직접적으로 사용 불가

if (typeof value === 'string') {
	console.log(value.toUpperCase()); // 타입 체크 후 사용 가능
}

unknown은 안전한 방식으로 값을 처리하기 위해 먼저 타입을 확인해야 하므로 any보다는 더 안전한 타입이다.

 

2. 유니언 타입

유니언 타입은 두 가지 이상의 타입을 하나로 합쳐서 여러 타입 중 하나의 값을 가질 수 있도록 하는 TS의 기능이다. 즉, 변수나 함수가 여러 타입을 가질 수 있을 때 유용하게 사용할 수 있다.

let value: string | number;

value = 'hello'; // 문자열 가능
value = 42; // 숫자 가능

value 변수는 문자열 또는 숫자를 가질 수 있다. 즉 string 또는 number 중 하나의 타입이 할당될 수 있다.

또한 유니언 타입은 함수의 매개변수와 반환값에서도 사용할 수 있다.

function pringValue(value: string | number) {
	console.log(value);
}

printValue('hello'); 
printValue(123);

이 함수는 string 또는 number 타입의 값을 받아서 출력한다. 두 가지 타입 모두 허용되므로, 다양한 타입의 입력을 처리할 수 있다. 유니언 타입을 사용할 때는 각 타입별로 적절한 처리를 해야 한다. 모든 타입에 공통적으로 적용할 수 있는 연산이나 메서드를 사용할 수 있지만,  특정 타입에만 적용되는 메서드는 타입체크가 필요하다.

function process(value: string | number) {
	if (typeof value === 'string') {
    	console.log(value.toUpperCase()); // 문자열 관련 메서드 사용가능 - 대문자로 변환
    } else {
    	console.log(value.toFixed(2)); // 숫자일 때 처리 - 소수점 2자리까지 출력
    }
}

typeof 연산자를 사용하여 값이 string인지 number인지 확인하고, 각각에 맞는 메서드를 적용하고 있다. TS에서 value에 타입이 string인지 number인지 모르는 상태에서 런타임에서 타입을 검사하는 로직 없이 실행하려하면 경고를 띄운다. 하지만 유니언 타입을 사용한다고 해서 런타임에서 타입을 검사하는 로직이 항상 필요한 것은 아니다.

 

유니언 타입은 배열에서도 사용할 수 있다. 배열이 여러 타입의 요소를 포함할 수 있을 때 유니언 타입을 지정할 수 있다.

let values: (string | number)[] = ['Hello', 42, 'World', 100];

console.log(values);

이 경우 배열은 문자열과 숫자를 모두 포함할 수 있다.

유니언 타입의 활용 예시는 다음과 같다.

API응답이 여러 가지 형태로 올 수 있는 상황에서 유니언 타입을 사용해 대응할 수 있다.

type SuccessResponse = {
	status: 'success';
    data: string[];
};

type ErrorResponse = {
	status: 'error';
    message: string;
};

function handleResponse(response: SuccessResponse | ErrorResponse) {
	if (response.status === 'success') {
    	console.log('Data:', response.data);
    }else {
    	console.log('Errpr:', response.message);
    }
}

이 예시에서, response가 성공 또는 오류 응답 중 하나일 수 있으며, 각각의 경우에 맞는 처리를 유니언 타입을 통해 쉽게 구현할 수 있다. 유니언 타입의 장점으로는 다음과 같다.

  • 다양한 값을 처리할 수 있어 유연한 코드를 작성할 수 있다.
  • 타입 안전성을 유지하면서도 여러 타입의 값에 대해 유연한 처리를 할 수 있다.
  • 가독성이 높아지고 코드의 유지보수가 용이해진다.

3. 리터럴 타입

리터럴 타입은 TS에서 고정된 값을 타입으로 사용하는 개념이다. 즉, 변수나 상수의 값이 특정 값으로 제한될 때 사용된다. 리터럴 타입은 주로 문자열, 숫자, 불리언리터럴에 사용되며, 이를 통해 보다 엄격한 타입 체크를 할 수 있다.

let greeting: 'hello';
greeting = 'hello'; // 정상 작동
greeting = 'hi'; // 오류, 'hello'만 허용

위 코드에서 greeting 변수는 문자열 리터럴 타입인 'hello'로 타입이 고정되어 있다. 따라서 'hello' 이외의 값은 할당할 수 없다. 리터럴 타입은 다음과 같은 데이터 타입에서 사용할 수 있다.

  • 문자열 리터럴 타입: 고정된 문자열 값만 허용
  • 숫자 리터럴 타입: 고정된 숫자 값만 허용
  • 불리언 리터럴 타입: true또는 false로 제한
let direction: 'left' | 'right';
direction = 'left'; // 정상
direction = 'right'; // 정상
direction = 'up'; // 오류: 'left' 또는 'right'만 허용

let quantity: 1 | 2 | 3;
quantity = 1; // 정상
quantity = 4; // 오류: 1, 2, 3만 허용

let isDone: true;
isDone = true; // 정상
isDone = false; // 오류: true만 허용

리터럴 타입은 주로 유니언 타입과 결합하여 사용할 때 유용하다. 여러 개의 리터럴 타입을 유니언으로 묶으면, 특정한 값들 중 하나만 허용하는 방식으로 사용할 수 있다.

type Shape = 'circle' | 'square' | 'triangle';

let myShape: Shape;
myShape = 'circle'; // 정상
myShape = 'hexagon'; // 오류: 'circle', 'square', 'triangle'만 허용

여기서 Shape는 circle, square, triangle 중 하나만 가질 수 있는 유니언 리터럴 타입이다.

함수 인자를 리터럴 타입으로 제한하여, 함수가 특정한 값만 받을 수 있게 할 수도 있다. 이를 통해 함수 호출 시 잘못된 값이 전달되는 것을 방지할 수 있다.

function move(direction: 'left' | 'right' | 'up' | 'down') {
	console.log(`Moving ${direction}`);
}

move('left'); // 정상
move('right'); // 정상
move('forward'); // 오류: left, right, up, down만 허용

리터럴 타입은 상수와 함께 사용하면 불변성을 가지는 변수를 선언할 수 있다. const로 선언된 변수는 리터럴 타입으로 추론된다.

const name = 'Lois';
let newName: typeof name; // newName은 'Lois'로 타입이 고정됨
newName = 'Lois'; // 정상
newName = 'john'; // 오류: 'Lois'만 가능

여기서 name 변수는 const로 선언되었기 때문에 값이 'Lois'로 고정되고, typeof name을 사용하면 그 값을 리터럴 타입으로 추론한다.

리터럴 타입을 사용하면 고정된 값만 허용하므로, 코드의 타입 안전성을 크게 향상시킬 수 있다. 특히 유효한 값들을 미리 정의해두고 그 값들만 허용하는 상황에서 유용하다.

  • 잘못된 값을 사전에 방지할 수 있어 버그 발생 가능성을 줄일 수 있다.
  • 유연성이 필요 없는 상황에서 정확한 값을 강제할 수 있다.

리터럴 타입은 상태관리와 같은 다양한 상황에서 유용하게 사용할 수 있다.

type Status = 'loading' | 'success' | 'error';

function updateStatus(status: Status) {
	console.log(`Current status: ${status}`);
}

updateStatus('loading); // 정상
updateStatus('completed'); // 오류: loading, success, error만 하용

이 예시에서는 Status 타입이 특정한 세 가지 상태만 가질 수 있으며, 이 상태 중 하나만 함수에 전달될 수 있다.

4. 타입 알리어스

타입 알리어스는 TS에서 타입에 별칭(또는 이름)을 붙여주는 기능이다. 즉, 특정 타입을 복잡하게 여러 번 반복해서 쓰지 않고, 별도로 이름을 붙여서 재사용할 수 있도록 해준다. 이를 통해 코드의 가독성도 높아지고 유지보수도 쉬워진다. 하지만 보통 유니언 타입에서만 타입 알리어스를 쓰는 것이 낫다. 별칭을 잘 짓지 않아도 string 타입은 의도가 분명하다. 

type ID = string | number;

let userId: ID;
userId = 123; // 숫자 가능
userId = 'abc'; // 문자열 가능

또한 객체 타입에 대해서도 유용하다.

type User = {
	name: string;
    age: number;
    isActive: boolean;
}

let user1: User = {
	name: 'Lois',
    age: 25,
    isActive: true
};

타입 알리어스는 다음과 같은 장점을 가진다.

  • 가독성: 복잡한 타입을 쉽게 이해할 수 있도록 이름을 붙여서 코드가 더 읽기 쉬워진다.
  • 재사용성: 자주 사용하는 타입을 반복해서 작성할 필요 없이, 한 번 정의한 타입 알리어스를 재사용할 수 있다.
  • 유연성: 타입 알리어스를 통해 간결한 코드 작성이 가능하며, 복잡한 타입 구조도 단순하게 관리할 수 있다.

'Front-end > TypeScript' 카테고리의 다른 글

TypeScript 기본 (3)  (0) 2024.10.21
TypeScript 기본  (0) 2024.10.15