TypeScript 5장 - 러닝 타입스크립트(4)
포스트
취소

TypeScript 5장 - 러닝 타입스크립트(4)

TypeScript

image

Part.2

chapter.6 배열

  • 자바스크립트의 배열은 매우 유연하고 내부에 모든 타입의 값을 혼합해서 저장할 수 있다.
  • 그러나 대부분의 배열은 하나의 특정 타입의 값만 가진다.

    1
    2
    3
    4
    
    const elements = [true, null, undefined, 42];
    
    elements.push("even", ["more"]);
    // elements : [true, null, undefined, 42, "even", ["more"]]
    
  • 다른 타입의 값을 추가하게 되면 배열을 읽을 때 혼란을 줄 수 있으며, 오류가 발생할 수도 있다.
  • 타입스크립트는 초기 배열에 어떤 데이터 타입이 있는지 기억하고, 배열이 해당 데이터 타입에서만 작동하도록 제한한다.

    1
    2
    3
    4
    5
    6
    7
    
    const warriors = ["A", "B"];
    
    warriors.push("C");
    // OK : "C"의 타입은 string
    
    warriors.push(true);
    // Error: Argument of type "boolean" is not assignable to parameter of type "string".
    

6.1 배열 타입

  • 다른 변수 선언과 마찬가지로 배열을 저장하기 위한 변수는 초깃값이 필요하지 않다.
  • 변수는 undefined로 시작해서 나중에 배열 값을 받을 수 있다.
  • 변수에 타입 애너테이션을 제공해 배열이 포함해야 하는 값의 타입을 알려주려 한다.
  • 배열에 대한 타입 애너테이션은 배열의 요소 타입 다음에 []가 와야 한다.

    1
    2
    3
    
    let arrayOfNumbers: number[];
    
    arrayOfNumbers = [4, 8, 15, 16, 23, 42];
    
6.1.1 배열과 함수 타입
  • 배열 타입은 함수 타입에 무엇이 있는지를 구별하는 괄호가 필요한 구문 컨테이너의 예이다.
  • 괄호는 애너테이션의 어느 부분이 함수 반환 부분이고 어느 부분이 배열 타입 묶음인지를 나타내기 위해 사용한다.

    1
    2
    3
    4
    5
    
    let createStrings: () => string[];
    // 타입은 string 배열을 반환하는 함수
    
    let stringCreators: (() => string)[];
    // 타입은 각각의 string을 반환하는 함수 배열
    
6.1.2 유니언 타입 배열
  • 유니언 타입으로 배열 타입을 사용할 때 애너테이션의 어느 부분이 배열의 콘텐츠이고 어느 부분이 유니언 타입 묶음인지를 나타내기 위해 괄호를 사용해야 할 수도 있다.
  • 유니언 타입 배열에서 괄호 사용은 매우 중요하다.

    1
    2
    3
    4
    5
    
    let stringOrArrayOfNumbers: string | number[];
    // 타입은 string 또는 number의 배열'
    
    let arrayOfStringOrNumbers: (string | number)[];
    // 타입은 각각 number 또는 string인 요소의 배열
    
  • string 값과 undefined 값을 모두 가지는 타입의 예시
1
const nameMaybe = ["A", "B", undefined];
6.1.3 any배열의 진화
  • 초기에 빈 배열로 설정된 변수에서 타입 애너테이션을 포함하지 않으면 타입스크립트는 배열을 any[]로 취급하고 모든 콘텐츠를 받을 수 있다.
  • any변수가 변경되는 것처럼 any[]배열이 변경되는 것도 좋아하지 않는다.
  • 타입 애너테이션이 없는 빈 배열은 잠재적으로 잘못된 값 추가를 허용해 타입스크립트의 검사기가 갖는 이점을 부분적으로 무력화한다.
    1
    2
    3
    4
    5
    6
    
    let values = [];
    // 타입: any[]
    values.push("");
    // 타입: string[]
    value[0] = 0;
    // 타입: (number|string)[]
    
6.1.4 다차원 배열
  • 2차원 배열 또는 배열의 배열은 두 개의 [] 대괄호를 갖는다.

    1
    2
    3
    4
    5
    6
    7
    8
    
    let arrayOfArrayOfNumbers: number[][];
    // number[][] === (number[])[]
    
    arrayOfArrayOfNumbers = [
      [1, 2, 3],
      [4, 5, 6],
      [7, 8, 9],
    ];
    

6.2 배열 멤버

  • 타입스크립트는 배열의 멤버를 찾아서 해당 배열의 타입 요소를 되돌려주는 전형적인 인덱스 기반 접근 방식이다.

    1
    2
    3
    4
    
    const defenders = ["A","B"];
    
    const defenders - defenders[0];
    // 타입: string
    
  • 유니언 타입으로 된 배열의 멤버는 그 자체로 동일한 유니언 타입이다.

    1
    2
    3
    4
    
    const soldiersOrDates = ["A", new Date(1782, 6, 3)];
    
    const soldierOrDate = soldiersOrDates[0];
    // 타입: string | Date
    
6.2.1 주의 사항: 불안정한 멤버
  • 타입스크립트 타입 시스템은 기술적으로 불안정하다고 알려져 있다.
  • 대부분 올바른 타입을 얻을 수 있지만,ㄷ 때로는 값 타입에 대한 타입 시스템의 이해가 올바르지 않을 수 있다.
  • 기본적으로 타입스크립트는 모든 배열의 멤버에 대한 접근이 해당 배열의 멤버를 반환한다고 가정하지만, 자바스킙트에서조차도 배열의 길이보다 큰 인덱스로 배열 요소에 접근하면 undefined 를 제공한다.
  • 타입스크립트에는 배열 조회를 더 제한하고 타입을 안전하게 만드는 noUncheckedIndexedAccess 플래그가 있지만, 이 플래그는 매우 엄격해 대부분의 프로젝트에서 사용하지 않는다.

    1
    2
    3
    4
    5
    
    function withElements(elements: string[]) {
      console.log(elements[9000].length); // 타입 오류 없음
    }
    
    withElements(["It", "over"]);
    

6.3 스프레드와 나머지 매개변수

6.3.1 스프레드
  • 서로 다른 타입의 두 배열을 함께 스프레드해 새 배열을 생성하면 새 배열은 두 개의 원래 타입 중 어느 하나의 요소인 유니언 타입 배열로 이해된다.
    1
    2
    3
    4
    5
    6
    
    const soldiers = ["A", "B", "C"];
    // 타입: string[]
    const soldierAges = [90, 19, 45];
    // 타입: number[]
    const conjoined = [...soldiers, ...soldierAges];
    // 타입: (string|number)[]
    
6.3.2 나머지 매개변수 스프레드
  • 타입스크립트는 나머지 매개변수로 배열을 스프레드하는 자바스크립트 실행을 인식하고 이에 대한 타입 검사를 수행한다.
  • 나머지 매개변수를 위한 인수로 사용되는 배열은 나머지 매개변수와 동일한 배열 타입을 가져야 한다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    function logWarriors(greeting: string, ...names: string[]) {
      for (const name of names) {
        console.log(`${greeting}, ${name}`);
      }
    }
    
    const warriors = ["A", "B", "C"];
    
    logWarriors("Hello", ...warriors);
    
    const birthYears = [1844, 1840, 1583];
    
    logWarriors("Born in", ...birthYears);
    // Error: Argument of type 'number' is not assignable to parameter of type 'string'.
    

6.4 튜플

  • 자바스크립트의 배열은 이론상 어떤 크기라도 될 수 있지만, 튜플이라고 하는 고정된 크기의 배열을 사용하는 것이 유용하다.
  • 튜플 배열은 각 인덱스에 알려진 특정 타입을 가지며 배열의 모든 가능한 멤버를 갖는 유니언 타입보다 더 구체적이다.
  • 튜플 타입을 선언하는 구문은 리터럴처럼 보이지만 요소의 값 대신 타입을 적는다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    let yearAndWarrior: [number, string];
    // 0번째 인덱스에 number 타입을 갖고, 1번째 인덱스에 string 타입을 갖는다.
    yearAndWarrior = [530, "A"]; // OK
    
    yearAndWarrior = [false, "A"];
    // Error: Type "boolean" is not assignable to type "number".
    
    yearAndWarrior = [530];
    // Error: Type '[number]' is not assignable to type '[number, string]'.
    // Source has 1 element(s) but targer requires 2.
    
  • 단일 조건을 기반으로 두 개의 변수에 초깃값을 설정하는 것처럼 한 번에 여러 값을 할당하기 위해 튜플과 배열 구조 분해 할당을 함께 자주 사용한다.
    1
    2
    3
    
    let [year, warrior] = Math.random() > 0.5 ? [340, "A"] : [1827, "B"];
    // year 타입: number
    // warrior 타입: string
    
6.4.1 튜플 할당 가능성
  • 타입스크립트에서 튜플 타입은 가변 길이의 배열 타입보다 더 구체적으로 처리된다.
  • 가변 길이의 배열 타입은 튜플 타입에 할당할 수 없다.

    1
    2
    3
    4
    5
    6
    7
    8
    
    const pairLoose = [false, 123];
    // 타입: (boolean|number)[]
    const pairTupleLoose: [boolean, number] = pairLoose;
    // Error: Type '(number|boolean)[]' is not assignable to type '[boolean,number]'.
    // Target requires 2 element(2) but source may have fewer.
    
    const example: [number, string] = [1, "1"];
    const practice: [number, string] = example; // OK
    
    • pairLoose가 [boolean, number] 자체로 선언된 경우 pairTupleLoose에 대한 값 할당이 허용됐을 것이다.
    • 하지만 타입스크립트는 튜플 타입의 튜플에 얼마나 많은 멤버가 있는지 알고 있기 때문에 길이가 다른 튜플은 서로 할당할 수 없다.
  • 튜플은 구체적인 길이와 요소 타입 정보를 가지는 배열로 간주되므로 함수에 전달할 인수를 저장하는 데 특히 유용하다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    function logPair(name: string, value: number) {
      console.log(`${name} has ${value}`);
    }
    
    const pairArray = ["Amage", 1];
    // (string|number)[]로 인식
    logPair(...pairArray);
    // Error: A spread argument must either have a tuple type or be passed to a rest
    // parameter.
    
    const pairTupleIncorrect: [number, string] = [1, "Amage"];
    
    logPair(...pairTupleIncorrect);
    // Error: Argument of type 'number' is not assignable to parameter
    // of type 'string'.
    
    const pairTupleCorrect: [string, number] = ["Amage", 1];
    
    logPair(...pairTupleCorrect); // OK
    
    • logPair 함수의 매개변수는 string과 number로 입력된다.
    • (string|number)[] 타입의 값을 인수로 전달하려고 하면 둘 다 동일한 타입이거나 타입의 잘못된 순서로 인해 내용이 일치하지 않을 가능성이 있어 타입의 안전을 보장할 수 없다.
    • 그러나 값이 [string, number] 튜플이라고 알고 있다면 값이 일치한다는 것을 알게 된다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    function logTrio(name: string, value: [number, boolean]) {
      console.log(`${name} has ${value[0]} ${value[1]}`);
    }
    
    const trios: [string, [number, boolean]][] = [
      ["Amanitore", [1, true]],
      ["theFlead", [2, false]],
      ["Ann E", [3, false]],
    ];
    
    trios.forEach((trio) => logtrio(...trio)); // OK
    
    trios.forEach(logTrio);
    // Error: Argument of type '(name: string, value: [number, boolean]) => vild
    
    • 여러 번 함수를 호출하는 인수 목록을 배열에 저장해 함께 사용할 수 있다.
    • 다음 코드의 trios는 튜플 배열이고, 각 튜플은 두 번째 멤버로 또 튜플을 가진다.
    • trios.forEach(trio => logTrio(...trio))는 각 …trio가 logTrio의 매개변수 타입과 일치하므로 안전한 것으로 알려진다.
    • trio.forEach(logTrio)는 첫 번째 매개변수로 타입이 string인 [string, [number, boolean]] 전체를 전달하려고 시도하기 때문에 할당할 수 없다.
6.4.2 튜플 추론
  • 타입스크립트는 생성된 배열을 튜플이 아닌 가변 길이의 배열로 취급한다.
  • 배열의 변수의 초깃값 또는 함수에 대한 반환값으로 사용되는 경우, 고정된 크기의 튜플이 아니라 유연한 크기의 배열로 가정한다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    function firstCharAndSize(input: string) {
      return [input[0], input.length];
    }
    // 반환 타입: (string|number)[]
    
    const [firstChar, size] = firstCharAndSize("Gudit");
    // firstChar 타입: string|number
    // size 타입: string|number
    // 구조분해할당 firstCharAndSize("Gudit")
    // firstChar = "Gudit", size = 5
    
    • firstCharAndSize 함수는 [string, number]가 아니라 (string|number)[]를 반환하는 것으로 유추된다.
  • 명시적 튜플 타입

    • 함수에 대한 반환 애너테이션처럼 튜플 타입도 타입 애너테이션에 사용할 수 있다.
    • 함수가 튜플 타입을 반환한다고 선언되고, 배열 리터럴을 반환한다면 해당 배열 리터럴은 일반적인 가변 길이의 배열 대신 튜플로 간주된다.
    1
    2
    3
    4
    5
    6
    7
    8
    
    function firstCharAndSizeExplicit(input: string): [string, number] {
      return [input[0], input.length];
    }
    // 반환 타입: [string, number]
    
    const [firstChar, size] = firstCharAndSizeExplicit("Gudit");
    // 위의 에시와 달리, 반환 타입을 정확하게 명시하고 있다.
    // firstChar = "Gudit"(string), size = 5(number)
    
  • const 어서션

    • 명시적 타입 애너테이션에 튜플 타입을 입력하는 작업은 명시적 타입 애너테이션을 입력할 떄와 동일한 이유로 고통스러울 수 있다.
    • 코드 변경에 따라 작성 및 수정이 필요한 구문을 추가해야한다.
    • 대안으로 타입스크립트는 값 뒤에 넣을 수 있는 const 어서션인 as const 연산자를 제공한다.
    • const 어서션은 타입스크립트에 타입을 유추할 때 읽기 전용이 가능한 값 형식을 사용하도록 지시한다.
    1
    2
    3
    4
    
    const unionArray = [1157, "Tom"];
    // 타입: (string|number)[]
    const readonlyTuple = [1157, "Tom"] as const;
    // 타입: readonly [1157, "Tom"]
    
    • const 어선션은 유연한 크기의 배열을 고정된 크기의 튜플로 전환하는 것을 넘어, 해당 튜플이 읽기 전용이고 값 수정이 예상되는 곳에서 사용할 수 없음을 나타낸다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    const pairMutable: [number, string] = [1157, "Tom"];
    pairMutable[0] = 1247; // OK
    // 배열의 값을 수정하는 것은 const여도 가능하다.
    
    const pairAlsoMutable: [number, string] = [1157, "Tom"] as const;
    // Error: The type 'readonly [1157, "Tom"]' is 'readonly'
    // and cannot be assigned to the mutable type '[number, string]'.
    // 튜플과 readonly가 동시에 적용되기 때문에 에러가 뜬다.
    
    const pairConst = [1157, "Tom"] as const;
    pairConst[0] = 1247;
    // as const 를 사용하면 배열이어도 수정이 불가능하다.
    // Error: Cannot assign to '0' because it is a read-only property.
    
    • pairMutable은 전형적인 명시적 튜플 타입이므로 수정될 수 있다.
    • as const는 값이 변경되리 수 있는 pairAlsoMutable에 할당할 수 없도록 하고, 상수 pairConst의 멤버는 수정을 허용하지 않는다.
    1
    2
    3
    4
    5
    6
    7
    
    function firstCharAndSizeAsConst(input: string) {
      return [input[0], input.length] as const;
    }
    // 반환 타입: readonly [string, number]
    
    const [firstChar, size] = firstCharAndSizeAsConst("Shin");
    // firstChar 타입: string, size 타입: number
    
    • firstCharAndSizeAsConst는 읽기 전용 [string, number]를 반환한다.
    • 해당 튜플에서 값을 찾는 것에만 관심을 둔다.

6.5 마치며

  • []로 배열 타입을 선언할 수 있다.
  • 괄호를 사용해 함수의 배열 또는 유니언 타입의 배열을 선언할 수 있다.
  • 타입스크립트가 배열 요소를 배열의 타입으로 이해하는 방법
  • …스프레들와 나머지 매개변수로 작업하는 방법
  • 고정된 크기의 배열을 나타내는 튜플 타입 선언하기
  • 타입 애너테이션 또는 as const 어서션으로 튜플 생성하기
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.