티스토리 뷰

 

공부를 시작하며

타입스크립트에서 객체의 타입을 지정하는 방법에 대해서 공부를 시작합니다.

타입스크립트 문서를 통해서 공부를 했습니다.

레퍼런스 : https://www.typescriptlang.org/docs/handbook/2/objects.html 

 

Documentation - Object Types

How TypeScript describes the shapes of JavaScript objects.

www.typescriptlang.org

 

 

일반적으로 객체에 타입을 지정

익명 타입으로 지정하는 방법

function printUser(user: { name: string; age: number }) {
  console.log(`My name is ${user.name}. I'm ${user.age} years old.`);
}

 

인터페이스로 지정하는 방법

interface User {
  name: string;
  age: number;
}

function printUser(user: User) {
  console.log(`My name is ${user.name}. I'm ${user.age} years old.`);
}

 

타입 별칭으로 지정하는 방법

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

function printUser(user: User) {
  console.log(`My name is ${user.name}. I'm ${user.age} years old.`);
}

 

속성 수정자 Property Modifiers

 

선택적 속성 (Optional Property)

선택적으로 부여해야하는 속성(property)과 같은 경우에는 ?를 사용합니다.

interface User {
  name: string;
  age?: number;
  address?: string;
}

function printUser(user: User) {
  // ...
}

printUser({ name: "Jone" });
printUser({ name: "Jone", age: 20 });
printUser({ name: "Jone", age: 20, address: "South Korea" });

 

 

잠재적 undefined

선택적 속성의 값은 잠재적으로 undefined 라고 인식합니다.

interface User {
  name: string;
  age?: number;
  address?: string;
}

function printUser(user: User) {
  const age = user.age;
}

 

따라서 다음과 같이 undefined 일 때의 처리가 필요합니다.

이때 여러 방법으로 처리가 가능하겠지만 간단하게 삼항 연산자로 해결이 가능합니다.

function printUser(user: User) {
  const age = user.age === undefined ? 0 : user.age;
}

 

객체 구조 분해와 기본값 할당

더 쉽게는 객체 구조 분해로 기본값을 할당하여 해결도 가능합니다.

function printUser({ name, age = 0, address = "" }: User) {
  const getAge = age;
  // (parameter) age: number
}

 

readonly 속성

readonly 속성을 사용해서 오직 값을 읽는데에만 사용할 수 있도록 설정할 수 있습니다.

해당 속성이 추가된 속성을 임의로 변경하려 하면 타입스크립트에서는 오류를 표시해줍니다.

interface ReadonlyType {
  readonly property: string;
}

function readOnly(option: ReadonlyType) {
  console.log(option.property);

  option.property = "test";
  // Cannot assign to 'property' because it is a read-only property.
}

 

readonly가 변경이 될 수 도 있는 상황

readonly 속성을 사용하면 항상 변경이 되지 않는다는 뜻은 아닙니다. 다음과 같이 객체의 내부 속성의 값을 변경하려고 하면 오류가 표시되지 않습니다.

interface User {
  name: string;
  readonly inform: { age: number };
}

function printUser(user: User) {
  user.inform.age++;
}

 

readonly를 타입 별칭을 통해서 지정하고 수정하기

interface User {
  name: string;
  age: number;
}
interface ReadonlyUser {
  readonly name: string;
  readonly age: number;
}

let writeableUser: User = {
  name: "Jone",
  age: 24,
};

let readonlyUser: ReadonlyUser = writeableUser;

console.log(readonlyUser.age);
// 24
writeableUser.age = 30;
console.log(readonlyUser.age);
// 30

readonlyUser.age = 30;
// Cannot assign to 'age' because it is a read-only property.

 

인덱스 서명 ( Index Signatures )

타입의 프로퍼티의 이름을 다 모르지만 유형을 알고 있을때 인덱스 서명(index signatures)를 사용합니다.

다음은 배열의 인덱스 서명 예시입니다. 배열의 index는 number 타입이고 각 배열의 아이템들을 number 타입으로 지정할 때 예시입니다.

interface NumberArray {
  [index: number]: number;
}

const numberArray: NumberArray = [];

numberArray[0] = 1;

numberArray[1] = "2";
// Type 'string' is not assignable to type 'number'.

 

readonly 를 통해서 인덱스 할당을 방지할 수 있습니다.

interface NumberArray {
  readonly [index: number]: number;
}

const numberArray: NumberArray = [];

numberArray[0] = 10;
// Index signature in type 'NumberArray' only permits reading.

 

타입 확장 ( Extending Types )

다음과 같이 shape 속성을 가지는 타입이 있습니다.

interface Shape {
  x: number;
  y: number;
  fill: string;
}

그리고 사각형의 속성을 가지는 타입이 있습니다. 타입의 프로퍼티가 shape에서 가지고 있는 속성을 반복적으로 가지고 있습니다.

interface Rect {
  type: "rect";
  x: number;
  y: number;
  fill: string;
  width: number;
  height: number;
}

이럴때 shape 타입의 속성을 가지는 타입을 확장할 수 있습니다.

 

extends

다음과 같이 extends (확장)이라는 구문으로 속성을 확장할 수 있습니다. 다음의 Rect 인터페이스는 Shape 타입의 속성들을 포함하게 됩니다.

interface Shape {
  x: number;
  y: number;
  fill: string;
}

interface Rect extends Shape {
  type: "rect";
  width: number;
  height: number;
}

 

2개 이상 extends

2개 이상의 인터페이스를 확장하기 위해서는 , 를 통해서 확장할 수 있습니다.

interface Shape {
  x: number;
  y: number;
}

interface Styles {
  fill: string;
  stroke?: string;
}

interface Rect extends Shape, Styles {}

 

 

Intersection Types 타입 교차

interface를 통해서 타입을 확장하는 것 외에 타입스크립트에서는 & 연산자를 통해서 교차로 확장할 수 있는 방법을 제공해줍니다.

interface Shape {
  x: number;
  y: number;
}

interface Styles {
  fill: string;
  stroke?: string;
}

type Rect = Shape & Styles & {};

 

 

 

제네릭을 통한 타입 확장

Before

다음과 같이 비슷하지만 다른 타입이 3가지 있습니다.

비슷한 타입이 여러개 생성이 되면 타입 별로 타입을 생성해 줘야할 것입니다.

다음 After 에서의 예시로 좀 더 나은 방법으로 타입을 유연하게 대처하는 방법을 살펴봅니다.

interface StringContents {
  content: string;
}
interface NumberContents {
  content: number;
}
interface BooleanContents {
  content: boolean;
}

 

After

다음과 같이 제네릭 타입을 사용해서 사용될 타입을 추론하도록 할 수 있습니다.

interface Contents<T> {
  content: T;
}

const obj: Contents<string> = {
  content: "this is string contents",
};

 

Array 타입에서의 제네릭

interface ArrayType<T> {
  array: T[];
}

const information: ArrayType<number> = {
  array: [1, 2, 3, 4, 5],
};

 

 

ReadonlyArray 타입

readonly 를 제네릭과 쓸 수 있는 특별한 타입인 ReadonlyArray 와 같은 타입이 있습니다.

다음과 같이 ReadonlyArray 에서 제네릭을 사용하는 것과 같이 사용합니다.

function onlyReadArray(array: ReadonlyArray<string>) {
  // 읽기
  const copy = array.slice();
  const lastValue = array[array.length - 1];

  // 변경
  array.push("123");
  // Property 'push' does not exist on type 'readonly string[]'.
}

 

튜플 타입 ( Tuple Types )

array 의 배열 갯수를 제한하는 방법중 튜플 타입이 있습니다.

다음과 같이 간단하게 선언 가능합니다.

type TupleNum = [number, number];

튜플 타입을 지정하면 배열 구조 분해를 통해서도 각 타입을 확인하고 사용할 수 있습니다.

type Tuple = [string, number];

function tupleArr(getTupleArr: Tuple) {
  const [string, number] = getTupleArr;
  // string : string
  // number : number
}

readonly tuple type

상수 단언으로 readonly 튜플 타입을 생성해 줄 수도 있습니다.

const tuple = [1, 2] as const;
// const tuple: readonly [1, 2]

 

optional tuple type

옵셔널하게 튜플 타입을 받을 수 있습니다. 간단하게 다음과 같이 받는 배열의 뒤에 ? 연산자를 붙이면 됩니다.

그리고 받는 튜플 타입의 length의 타입을 보게되면 다음과 같이 2 또는 3개의 갯수일 수 도 있다고 판단합니다.

type Points = [number, number, number?];

function addPoints(points: Points) {
  //...
  const [x, y, z] = points;
  // const z: number | undefined

  console.log("points length: ", points.length);
  // (property) length: 2 | 3
}

 

Rest Tuple type

다음과 같이 ... 스프레드 연산자를 사용해서 튜플 타입을 지정해줄 수 있습니다.

type StringNumberRestBoolean = [string, number, ...boolean[]];

type StringRestBooleanNumber = [string, ...boolean[], number];


let test1: StringNumberRestBoolean = ["1", 2, true, false, true];
let test2: StringRestBooleanNumber = ["1", true, false, true, 2];

 

댓글
최근에 올라온 글