웹개발/typescript

[타입스크립트] 배열 타입

joamashi 2024. 8. 30. 22:00

타입스크립트에서 배열은 특정 타입의 값들을 순서대로 모아놓은 자료구조입니다. 자바스크립트 배열과 유사하지만, 타입스크립트에서는 각 요소의 타입을 명시하여 더욱 강력한 타입 안전성을 제공합니다.

기본적인 배열 타입

  • 숫자 배열:
    let numbers: number[] = [1, 2, 3, 4];
  • 문자열 배열:
    let strings: string[] = ['apple', 'banana', 'cherry'];
    
  • 불리언 배열:
    let booleans: boolean[] = [true, false, true];
    

다양한 타입의 요소를 가진 배열

  • any 타입: 어떤 타입의 값이든 허용 (주의: 타입 안전성이 약화될 수 있음)
    let anyArray: any[] = [1, 'hello', true];
    
  • 유니온 타입: 여러 타입 중 하나를 가질 수 있는 요소
    let numberOrString: (number | string)[] = [1, 'two', 3];
    
  • 인터페이스를 사용한 배열:
    interface Person {
        name: string;
        age: number;
    }
    
    let people: Person[] = [
        { name: 'Alice', age: 30 },
        { name: 'Bob', age: 25 }
    ];
    

배열의 읽기 전용 만들기

  • readonly 키워드: 배열 요소의 값을 변경할 수 없도록 만듭니다.
    let readonlyNumbers: readonly number[] = [1, 2, 3];
    // readonlyNumbers.push(4); // 오류 발생
    

배열의 제네릭

  • 제네릭: 타입을 매개변수처럼 사용하여 재사용 가능한 코드를 작성
    function identity<T>(arg: T[]): T[] {
        return arg;
    }
    

튜플 (Tuple)

  • 튜플: 각 요소의 타입과 순서가 정해진 배열
    let tuple: [string, number] = ['hello', 10];

배열 관련 유용한 메서드

  • map, filter, reduce: 배열의 요소를 변환하거나 필터링하는 고차 함수
  • forEach: 배열의 각 요소에 대해 특정 함수를 실행
  • some, every: 배열의 요소들이 특정 조건을 만족하는지 확인

실제 예시: 사용자 정보 배열

interface User {
    id: number;
    name: string;
    isAdmin: boolean;
}

let users: User[] = [
    { id: 1, name: 'Alice', isAdmin: true },
    { id: 2, name: 'Bob', isAdmin: false }
];

// 관리자만 필터링
let admins = users.filter(user => user.isAdmin);

정리

타입스크립트의 배열은 자바스크립트 배열의 기능에 타입 안전성을 더하여 코드의 신뢰성을 높여줍니다. 다양한 타입의 요소를 허용하고, 읽기 전용으로 만들 수 있으며, 제네릭을 활용하여 재사용 가능한 코드를 작성할 수 있습니다. 튜플은 특정한 구조의 데이터를 표현하는 데 유용합니다.

핵심:

  • 타입 명시: 배열의 각 요소의 타입을 명확하게 지정
  • 다양한 타입 지원: 숫자, 문자열, 객체 등 다양한 타입의 요소를 포함
  • 읽기 전용: readonly 키워드를 사용하여 배열을 수정하지 못하도록 설정
  • 제네릭: 재사용 가능한 코드 작성
  • 튜플: 고정된 길이와 타입의 배열

다양하게 선언하기

지금까지 기본적인 배열 타입 선언 방법에 대해 알아보았습니다. 하지만 타입스크립트는 더욱 다양하고 유연한 방법으로 배열을 선언할 수 있도록 지원합니다.

1. 제네릭을 이용한 배열 타입

  • 재사용성: 특정 타입에 제한되지 않고 다양한 타입의 배열을 다룰 수 있는 함수나 클래스를 만들 수 있습니다.
  • 유연성: 코드를 더욱 일반화하여 활용 범위를 넓힐 수 있습니다.
function identity<T>(arg: T[]): T[] {
  return arg;
}

let myNumber: number[] = identity([1, 2, 3]);
let myString: string[] = identity(['hello', 'world']);

2. 인터페이스와 함께 사용하는 배열 타입

  • 복잡한 객체 배열: 인터페이스를 통해 객체의 구조를 정의하고, 배열의 요소 타입으로 사용하여 복잡한 데이터를 관리할 수 있습니다.
interface Person {
  name: string;
  age: number;
}

let people: Person[] = [
  { name: 'Alice', age: 30 },
  { name: 'Bob', age: 25 }
];

3. 튜플 (Tuple)

  • 고정된 길이와 타입: 각 요소의 타입과 순서가 정해진 배열입니다.
  • 특정한 데이터 구조 표현: 함수의 반환값이나 특정한 형식의 데이터를 나타낼 때 유용합니다.
let color: [number, number, number] = [255, 0, 0]; // RGB 색상

4. readonly 배열

  • 읽기 전용: 배열의 값을 변경할 수 없도록 만들어 데이터의 무결성을 보장합니다.
let numbers: readonly number[] = [1, 2, 3];
// numbers.push(4); // 오류 발생

5. 배열의 배열

  • 다차원 배열: 배열 안에 또 다른 배열을 포함하여 다차원 데이터를 표현할 수 있습니다.
let matrix: number[][] = [
  [1, 2, 3],
  [4, 5, 6]
];

6. 배열과 유니온 타입

  • 다양한 타입의 요소: 배열의 요소가 여러 타입일 수 있도록 허용합니다.
let mixed: (number | string)[] = [1, 'hello'];

7. 배열과 null/undefined

  • null/undefined 허용: 배열의 요소가 null 또는 undefined일 수 있도록 허용합니다.
let optionalNumbers: (number | null)[] = [1, null, 3];

8. 배열과 제네릭 제약

  • 타입 제약: 제네릭 타입에 제약을 추가하여 더욱 안전한 코드를 작성할 수 있습니다.
function log<T extends string | number>(arg: T[]): void {
  // T는 string 또는 number 타입만 허용
  console.log(arg);
}

이미 다양한 방법을 소개해 드렸지만, 좀 더 깊이 있게 살펴보고 추가적인 예시를 통해 타입스크립트의 유연성을 알아볼까요?

1. 제네릭 제약 강화하기

  • extends 키워드: 제네릭 타입의 상위 타입을 제한하여 더욱 구체적인 타입을 만들 수 있습니다.
  • keyof 키워드: 객체의 키 타입을 추론하여 사용할 수 있습니다.
// 특정 인터페이스를 구현하는 객체만 허용
interface User {
  name: string;
  age: number;
}

function getUsers<T extends User>(users: T[]): T[] {
  return users;
}

// 특정 프로퍼티만 접근 가능하도록 제한
type UserKeys = keyof User;
function getUserProperty<K extends UserKeys>(user: User, key: K): User[K] {
  return user[key];
}

2. 조건부 타입 활용하기

  • 조건에 따라 타입 변경: 특정 조건에 따라 다른 타입을 할당할 수 있습니다.
type IsString<T> = T extends string ? true : false;

function isStringArray<T>(arr: T[]): arr is string[] {
  return IsString<T[number]>;
}

3. 매핑된 타입 활용하기

  • 객체의 프로퍼티 타입 일괄 변경: 객체의 모든 프로퍼티 타입을 한 번에 변경할 수 있습니다.
interface Person {
  name: string;
  age: number;
}

type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

type ReadonlyPerson = Readonly<Person>;

4. 인덱스 시그니처 활용하기

  • 동적 프로퍼티: 배열의 요소 타입을 동적으로 지정할 수 있습니다.
interface StringArray {
  [index: number]: string;
}

let myArray: StringArray = ['hello', 'world'];

5. 리터럴 타입 활용하기

  • 정확한 값 지정: 특정 값만 허용하는 타입을 만들 수 있습니다.
type Direction = 'up' | 'down' | 'left' | 'right';
let directions: Direction[] = ['up', 'down'];

6. 배열 spread 연산자 활용하기

  • 배열 병합: 여러 배열을 하나의 배열로 합칠 수 있습니다.
let arr1: number[] = [1, 2];
let arr2: number[] = [3, 4];
let combined: number[] = [...arr1, ...arr2];

7. 배열 메서드와 함께 사용하기

  • map, filter, reduce 등: 배열을 변환하거나 새로운 배열을 생성할 때 유용합니다.
let numbers: number[] = [1, 2, 3];
let doubled: number[] = numbers.map(num => num * 2);

8. TypeScript의 타입 추론 활용하기

  • 자동 타입 추론: 명시적으로 타입을 지정하지 않아도 타입스크립트가 자동으로 타입을 추론합니다.
let numbers = [1, 2, 3]; // 타입이 number[]로 추론

 

 

728x90