Today I Learned/TIL 07

2023 - 07 - 24 TypeScript 타입스크립트 const, readonly, any, unknown, union

sangwoo_rhie 2023. 7. 27. 10:50

오늘 배운 것:

타입스크립트 let, const, readonly, any, unknown, union

 

 

let : 변수선언 (값을 변경할 수 있다.)

let num: number = 5;
console.log(num);  // 출력: 5

num = 10;
console.log(num);  // 출력: 10

 

const: 상수선언 (값을 변경할 수 없다. 즉 =로 재할당할 수 없다.)

const num: number = 5;
console.log(num);  // 출력: 5

num = 10;  // 에러: 'num'은 const로 선언되었으므로 다시 할당될 수 없음!
const nums: number[] = [];
console.log(nums);  // 출력: []
nums.push(1); // 할당은 되지 않아도 배열에 데이터를 추가/삭제하는 것은 문제가 안됨
nums.push(2); // 은근히 헷갈릴 수 있지만 = 연산자 기준으로만 생각하면 매우 쉬움
console.log(nums);  // 출력: [1, 2]

nums = [];  // 에러: 'nums'는 const로 선언되었으므로 다시 할당될 수 없다.

 

앞의 let과 const는 자바스크립트에서 많이 사용되는 키워드이지만,

readonly는 타입스크립트에서 등장한 키워드이다. 

 

readonly는 타입스크립트에서 객체의 속성을 불변으로 만드는 데 사용되어서,

클래스의 속성이나 인터페이스의 속성을 변경할 수 없게 만들 수 있다.

 

class Person {
  readonly name: string;   //  readonly를 통해 name의 타입을 string으로 정의함
  readonly age: number;  //  readonly를 통해 age의 타입을 number로 정의함

  constructor(name: string, age: number) {  // constructor 생성자를 통해 객체의 속성을 불변으로 만듦
    this.name = name;  
    this.age = age;
  }
}

const person = new Person('Spartan', 30);

console.log(person.name);  // 출력: 'Spartan'
console.log(person.age);   // 출력: 30

person.name = 'Jane';  // 에러: 'name'은 readonly 속성이므로 다시 할당할 수 없다
person.age = 25;       // 에러: 'age'은 readonly 속성이므로 다시 할당할 수 없다

 


1. any, unknown, union

어쩔 수 없이 가변적인 타입의 데이터를 저장하고 싶다면 any보다는, unknown을 쓰는 것이 좋고,

가변적인 타입을 일일이 정의할 수 있다면 union 사용이 가장 낫다.

 

- any는 모든 타입에 쓰일 수 있는 슈퍼 타입으로써, 자바스크립트의 object 타입과 같은 최상위 타입이다.

하지만, any 타입은 코드의 안정성과 유지 보수성을 저해할 수 있으므로, 가급적 사용하지 말아야 한다.

let anything: any;
anything = 5; // 최초에는 숫자를 넣었지만
anything = 'Hello'; // 문자열도 들어가고
anything = { id: 1, name: 'John' }; // JSON도 들어간다. (바람직하지 않음)

 

-any의 대체제 unknown

unknown 타입은 any 타입과 비슷한 역할을 하지만 더 안전한 방식으로 동작한다.

unknown 타입의 변수에도 모든 타입의 값을 저장할 수 있다. 하지만, 그 값을 다른 타입의 변수에 할당하려면 typeof 등으로 명시적으로 타입을 확인해야 한다.

 

let unknownValue: unknown = '나는 문자열입니다';
console.log(unknownValue); // 나는 문자열입니다!

let stringValue: string;
stringValue = unknownValue; // 에러 발생! unknownValue가 string임이 보장이 안되기 때문!
stringValue = unknownValue as string;

console.log(stringValue); // 나는 문자열입니다!

stringValue = unknownValue as string; 코드를 Type Assertion (타입 단언)이라고 한다.

unknown 타입의 변수를 다른 곳에서 사용하려면 타입 단언을 통해 타입 보장을 해서 사용할 수 있다.

 

let unknownValue: unknown = '나는 문자열입니다!';
let stringValue: string;

if (typeof unknownValue === 'string') {
  stringValue = unknownValue;
  console.log('unknownValue는 문자열이네요~');
} else {
  console.log('unknownValue는 문자열이 아니었습니다~');
}

 

이러한 unknown도 재할당을 할 때 타입 체크가 되므로 안전함을 보장한다. 하지만, unknown 타입도 결국 재할당이 일어나지 않으면 타입 안정성이 보장되지 않는다.

이럴 때를 위해 union 타입이라는 것이 사용된다. union은 여러 타입 중 하나를 가질 수 있는 변수를 선언할 때 사용되며, union은 | 연산자를 사용해 여러 타입을 결합하여 표현한다.

 

type StringOrNumber = string | number; // 원한다면 | boolean 이런식으로 타입 추가가 가능하다

function processValue(value: StringOrNumber) {
  if (typeof value === 'string') {
    // value는 여기서 string 타입으로 간주된다.
    console.log('String value:', value);
  } else if (typeof value === 'number') {
    // value는 여기서 number 타입으로 간주된다.
    console.log('Number value:', value);
  }
}

processValue('Hello');
processValue(42);