[React] TypeScript(3) - tuple, readonly, ⭐enum(feat.string), any와 unknown, generic, interface와 type

J.A.Y·2024년 3월 23일
0

TypeScript

목록 보기
3/4

TS 문법

본 글에서는 JS에는 없는, TS만의 문법들을 다뤄보도록 하겠습니다.

1. Tuple

Tuple은 불변의 sequence 자료형입니다. Tuple은 일반적으로 순서와 규칙이 있는 배열을 만들고자 할 때 사용하지요. JS의 배열과 비슷한 개념이나, 인덱스에 들어올 요소의 타입 지정해줘야 한다는 점이 다릅니다.

예를들어, 아래와 같이 0번째 인덱스의 타입을 string으로 지정해준 후 숫자를 할당하면 type error가 발생하게 됩니다.

let drink: [string, number] = ['사이다', 1000];
drink[0] = 1 

Tuple의 한계

배열의 순서와 규칙을 지정해 만들 순 있으나, 배열의 길이가 고정되어 있는 것이 아니라서 지정해 놓은 순서와 규칙이 쉽게 무너질 수 있다는 것이 tuple의 한계입니다.

예를 들어서, 위의 예시처럼 drink라는 배열을 두 개의 요소만 집어 넣고, 인덱스0은 string으로, 인덱스가 1은 number로 타입을 지정한 후 앞으로 계속 사용한다고 가정해보겠습니다.

그런데 만약 drink.push('콜라')라고 코드를 잘못 입력하는 순간 기존의 drink는 완전 새로운 순서와 규칙을 지닌 배열로 바뀌게 됩니다. 아무 타입도 지정하지 않은, '콜라'라는 새로운 값이 추가기 때문이지요.

그래서 만약 tuple로 배열을 만들고, 이 안의 순서와 규칙을 고정시키고 싶을 때는 다음으로 나오게 될 'readonly'를 사용하곤 합니다.

2. Readonly

Readonly는 '읽기 전용'상태로 만들어 줌으로써 값을 고정시켜줍니다. 즉, push든, 값을 변경하고자 시도하든 에러가 발생하게 되지요.

예를 들면 drink에 readonly를 추가한 후, drink[0]를 '사이다'대신 '콜라'로 바꾸려고 시도하면 아래와 같은 에러가 발생하게 됩니다.

const drink: readonly [string, number] = ['사이다', 1000];
drink[0] = '콜라';
// Error: Cannot assign to '0' because it is a read-only property.

3. ⭐Enum

Eum은 '명명된 값의 집합을 이루는 자료형'으로, 각 key에 의미있는 이름과 값을 부여할 수 있습니다. 이를 사용하면 별도의 타입을 만들 필요 없이 enum 그 자체를 타입처럼 활용할 수 있습니다.

예를 들어, CountryCode의 key에 국가 이름을 명명하고, 해당 국가 코드를 value로 줘보겠습니다.

enum CountryCode {
    'Argentina' = 'AR',
    'China' = 'CN',
    'Japan' = 'JP',
    'Korea' = 'KR',
    'Guam' = 'GU',
    'Guatemala' = 'GT',
    'Guinea' = 'GN',
    'UnitedStates' = 'US',
}

그리고 이를 마치 하나의 타입처럼, 긜고 동시에 CountryCode에 포함된 값을 사용할 수 있습니다.

const countryA: CountryCode = CountryCode.Korea
const countryB: ContryCode = CountryCode.Guinea
const countryList: CountryCode[] = [CountryCode.Korea, CountryCode.Guinea]

또한, 객체가 아님에도 불구하고 Object.entries나 Object.values, Object.keys의 적용이 가능합니다. (Enum을 사용하는 목적을 고려했을 때 직접 객체로 변환하는 것은 그닥 권장하는 방법은 아닙니다.)

  • Object.entries
const countryEntryList = Object.entries(CountryCode)
  • Oject.values
const countryList2: CountryCode[] = Object.values(CountryCode).filter((code) => code === CountryCode.Korea || code === CountryCode.Guinea)

Enum의 단점? number을 이용할 때

우하한기술 블로그를 보다가 Enum의 키값들을 number로 사용하면 몇 가지 오류가 있다는 사실을 알게 됐습니다.

예를 들어서, enum의 value타입을 number로 하면, 아래처럼 존재하지 않는 값을 입력해도 에러가 발생하지 않는다고 합니다.

enum Auth {
    admin = 0, 
    user = 1,
    guest = 2,
}

const personA: Auth = Auth.user
const personB: Auth = 2
const personC: Auth = 5 // 에러 X

또, Object.keys와 Object.entries를 사용할 수 없고, 양방향 매핑이 되어 예기치 못한 오류가 발생할 수 있다고 합니다.

따라서, enum은 number보단 되도록 string으로 사용하는 것이 안전한 것 같습니다.

4. Any와 Unknown

4-1. Any

Any는 값의 타입으로 어떠한 것이 올지 모르거나 정해진 타입이 없을 때 사용하거나, 타입스크립트에서 추론할 근거가 없을 때 암시적으로 any로 할당됩니다(후자의 경우엔 tsconfig에서 strictf를 true로 설정하거나, noImplicityAny를 ture로 해주면 any가 할당되는 대신 에러가 발생합니다.)

예를 들어서, val에 any로 타입을 설정하면, 어떤 타입이든 할당해줄 수 있습니다.

let val: any;
val = true;
val = 1;
val = 'str';
val = { name: 'codingon'};

// 암묵적으로 사용하는 방법
let val2;
val2 = false;
val2 = 'hi';

하지만 any의 문제점은 이를 남용하게 되면 타입스크립트를 사용하는 목적을 퇴색시키게 만든다는 것입니다.
실제로 타입 지정하는 일을 귀찮게 여기 우선 any로 선언한 후 작업하는 경우가 많다고 합니다. 이것이 반복되어 코드 전반에 쓰인다면 자바스크립트를 사용하는 것과 무엇이 다를까요?

그래서 정말 불가피하게 사용하는 경우가 아니고선 any를 사용하지 않는 것을 권장하는 추세라고 합니다.

🤔 하지만 타입을 단정짓기 힘든 상황이 있을 수 있잖아요? 이럴 때 any를 사용하면 되는 건가요?

Any를 사용할 수도 있지만, 이와 비슷하면서도 다른, type의 안전성을 높여줄 unknown을 사용하면 된다고 합니다.

4-2. Unknown : any의 대체

Unknown 또한 any처럼 top type으로 모든 값을 할당할 수 있습니다. 다만, any와의 차이점은 값을 사용하기 전에 추가적인 검사나 캐스팅이 필요하다는 것입니다. 만약 값의 타입으로 unknown을 지정해준 뒤 값의 타입을 검사하거나 선언해주는 코드가 없으면, 해당 값을 이용하려고 할 때 '아직은 타입을 알 수 없는 값이기에 사용할 수 없다'는 에러가 발생하게 됩니다.

아래는 unknown을 사용해서 input값이 객체인지 확인하는 예시입니다.

function printInfo(input: unknown): void {
    if (typeof input === 'object' && input !== null) {
        const { name, age } = input as { name: string, age: number };
        console.log(`이름: ${name}, 나이: ${age}`);
    } else {
        console.log('잘못된 입력입니다.');
    }
}

printInfo({ name: 'Alice', age: 30 }); // 이름: Alice, 나이: 30
printInfo('Bob'); // 잘못된 입력입니다.

Unknown을 사용해줌으로써 다른 타입과 더불어 객체 타입도 받을 수 있도록 하고, 그 후에 if 조건문을 통해 'input이 객체이면서 동시에 input이 null이 아닐 때만 이름과 나이가 출력되게 한다.'는 추가 검사를 통해 타입 안정성을 높이고 있습니다.

반면, 만약 여기서 unknown이 아닌 any를 사용했다면 위의 다음으로 나올 if문 조건을 더 자세히 처리해줘야할 것입니다. Any는 해당 값의 타입을 추가로 검사하거나 제한하지 않고 전부 허용하기 때문입니다. 즉, any를 사용하면 unknown을 사용할때보다 타입의 안정성이 저하되는 것이지요.

profile
Done is better than perfect🏃‍♀️

0개의 댓글