[Learning Typescript] Ch.6 배열

Typescript
Dragon C's avatar
Dec 20, 2023
[Learning Typescript] Ch.6 배열

자바스크립트 배열은 매우 유연하며 내부에 모든 타입의 값을 혼합하여 저장가능하다

  • 하지만, 대부분의 자바스크립트 배열은 하나의 특정 타입의 값만 가지며 다른 타입의 값을 추가할 시 배열을 읽을 때 프로그램 오류가 생길 수 있다.

  • 타입스크립트는 초기 배열에 어떤 데이터 타입이 있는지 기억하며, 배열이 해당 데이터 타입에서만 작동하도록 제한하여 배열의 데이터 타입을 하나로 유지시킨다.

const elements = [true, null, undefined, 42];
elements.push(”even”, [”more”]);
// 배열의 값 : [true, null, undefined, 42, “even”, [”more”]]

6.1 배열 타입

  • 다른 변수선언과 마찬가지로 배열을 저장하기 위한 변수는 초깃값이 필요하지 않다.

    • 변수시작은 undefined로 시작

  • 타입스크립트는 변수에 타입 애너테이션을 제공해 배열이 포함해야 하는 값의 타입을 알려준다

    let arrayOfNumbers: number[];
    
    arrayOfNumbers = [4, 8, 15, 16, 23, 42];

6.1.1 배열과 함수 타입

  • 배열 타입 : 함수 타입에 무엇이 있는지를 구별하는 괄호가 필요한 구문 컨테이너

  • 괄호: 애너테이션의 어느 부분이 함수 반환 부분이며 어느 부분이 배열 묶음인지를 나타냄

// 타입은 string 배열을 반환하는 함수
let createStrings: () => string[];

// 타입은 각각의 string을 반환하는 함수 배열
let stringCreators: (() => string)[];

6.1.2 유니언 타입 배열

  • 배열 각 요소가 여러 선택 타입 중 하나임을 나타낼때는 유니언 타입 사용

  • 괄호: 애너테이션의 어느 부분이 배열의 콘텐츠이고 어느 부분이 유니언 타입 묶음인지를 나타냄

  • 타입스크립트는 배열 선언에서 두가지 이상의 요소 타입이 포함되는 경우 유니언 타입 배열임을 알 수 있다.

// 타입은 string 또는 number의 배열
let stringOrArrayOfNumbers: string | number[];

//타입은 각각 number 또는 string인 요소의 배열
let arrayOfStringOrNumbers: (string | number)[];
//타입: (string | undefined)[]
const namesMaybe = [
	"Aqualtune",
	"Blenda",
	undefined,
];

6.1.3 any 배열의 진화

  • 초기에 빈 배열로 설정된 변수에 타입 애너테이션을 포함하지 않을 시 배열을 any[]로 받는다.

  • any 타입이 되도록 허용하면 타입스크립트의 타입 검사 목적을 부분적으로 무효화시키게 된다.(권장x)

//타입: any[]
let values = [];

//타입: string[]
value.push('');

//타입: (number | string)[]
values[0] = 0;

6.1.4 다차원 배열

  • 2차원 배열 또는 배열의 배열은 두개의 []를 갖는다.

  • 3차원은 세개의 []를 가지며 6차원은 6개의 []를 가져 그 이상 배열에는 몇개의 []가 필요한지 예측할 수 있다.

  • 원래의 타입을 가지며 끝에 []가 있고 그 뒤에 []를 추가한다

let arrayOfArraysOfNumbers = number[][];

arrayOfArraysOfNumbers = [
	[1, 2, 3],
	[2, 4, 6],
	[3, 6, 9],
];
  • 해당 다차원 배열은 다음과 같이 표현할수도 있다.

//타입: number[][]
let arrayOfArraysOfNumbers: (number[])[];

6.2 배열 멤버

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

//타입: string[]
const defenders = ["Clarenza", "Dina"];

//타입: string
const defender = defenders[0];
  • 유니언 타입으로 된 배열의 멤버는 자체로 동일한 유니언 타입

//soldierOrDates는 (string | Date)[] 타입이며 soldierOrDate는 string | Date 타입
const soldierOrDates = ["Deborah Sampson", new Date(1782, 6, 3)];

//타입: string | Date
const soldeirOrDate = soldiersOrDates[0];

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

6.3.1 스프레드

  • 스프레드: …스프레드 연산자를 사용해 배열을 결합하며 입력된 배열 중 하나의 값이 결과 배열에 포함될 것을 표시

  • 입력된 배열이 동일한 타입일 시 출력 배열도 동일한 타입

  • 서로 다른 타입의 두 배열을 통해 새 배열 생성 시 새 배열은 두개의 원래 타입 중 어느 하나의 요소인 유니언 타입 배열

/타입: string[]
const soldiers = ["Harriet Tubman", "Joan of Arc", "Khutulun"];

//타입: number[]
const soldierAges = [90, 19, 45];

//타입: (string | number)[]
//string 타입과 number타입 값을 모두 포함
const conjoined = [...soldiers, ...soldeirAges];

6.3.2 나머지 매개변수 스프레드

  • 타입스크립트는 나머지 매개변수로 배열을 스프레드하는 자바스크립트 실행 인식 및 타입 검사를 수행한다.

  • 나머지 매개변수에 인수로 사용되는 배열은 나머지 매개변수와 동일한 배열타입이여야 한다.

// …names 나머지 매개변수에는 string 값만 받으며 string[] 타입 배열을 스프레드 하는 것은 허용, 
// number[]는 허용되지 않는다.
function logWarriors(greeting: string, ...names: string[] {
	for (const name of names) {
		console.log('${greeting}, ${name}!');
	}
}

const warriors = ["Cathy Williams", "Lozen", "Nzinga"];

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 튜플

  • 자바스크립트 배열은 이론상 어떤 크기라도 될 수 있지만 때로는 고정된 크기의 배열을 사용하는 튜플을 사용하는 것이 유용하다.

  • 튜플 배열은 각 인덱스에 알려진 특정 타입을 가지고 유니언 타입보다 더 구체적이다.

  • 튜플 타입을 선언하는 구문은 요소의 값 대신 타입을 적는다.

//인덱스 0에 number 타입을 갖고 인덱스 1에 string 값을 갖는 튜플 타입 선언
let yearAndWarrior: [number, string];

yearAndWarrior = [530, "Tomyris"]; // Ok

yearAndWarrior = [false, "Tomyris"];
//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 target requires 2
  • 한번에 여러 값을 할당하기 위해 튜플과 배열 구조 분해할당을 함께 자주 사용한다.

// year 타입: 항상 number로 인식
// warrior 타입: 항상 string로 인식
let [year, warrior] = Math.random() > 0.5
	? [340, "Archidamia"]
	: [1828, "Rani of Jhansi"];

6.4.1 튜플 할당 가능성

  • 타입스크립트에서 튜플 타입은 가변길이의 배열 타입보다 구체적으로 처리되며 가변 길이의 배열 타입은 튜플 타입에 할당할 수 없다.

  • 우리눈에는 [boolean, number]를 바로 볼 수 있지만 타입스크립트는 일반적인 (boolean | number)[]타입으로 유추한다.

//타입: (boolean | number)[]
const pairLoose = [false, 123];

const pairTupleLoose: [boolean, number] = pairLoose;

//Error: Type '(number | boolean)[]' is not assignable to type '[boolean, number]'.
//Target requires 2 element(s) but source may have fewer.
//타입스크립트는 튜플 타입의 튜플에 얼마나 많은 멤버가 있는지 알고 있기에 길이가 다른 튜플은 서로 할당 X
const tupleThree: [boolean, number, string] = [false, 1583, "Nzinga"];

const tupleTwoExact: [boolean, number] = [tupleThree[0], tupleThree[1]];

const tupleTwoExtra: [boolean, number] = tupleThree;

// Error: Type '[boolean, number, string]' is not assignable to type '[boolean,
// number]'.
// Source has 3 element(s) but target allows only 2.
// tupleTwoExtra는 정확히 두개의 멤버를 가져야 하기에 tupleThree가 올바른 멤버로 시작하여도
// 세번째 멤버는 tupleTwoExtra에 할당할 수 없다.

  • 나머지 매개변수로서의 튜플

    • 튜플은 함수에 전달할 인수를 저장하는데 유용하다.

    • 타입스크립트는 … 나머지 매개변수로 전달된 튜플에 정확한 타입 검사를 제공할 수 있다.

//(string | number)[] 타입의 값을 인수로 전달하려고 할 시 둘 다 동일한 타입이거나 타입의 잘못된
//순서로 인해 내용이 일치하지 않을 가능성 때문에 타입의 안전 보장 X 
//값이[string, number] 튜플이라고 알 시 값이 일치하다는 것을 알 수 있다.
function logPair(name: string, value: number) {
	console.log('${name} has ${value}');
}

const pairArray = ["Amage", 1];

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
//나머지 매개변수 튜플을 사용할시 여러 번 함수를 호출하는 인수 목록을 배열에 저장하여 함께 사용가능
function logTrio(name: string, value: [number, boolean]) {
	console.log('${name} has ${value[0]} (${value[1]}');
}

//튜플 배열, 각 배열은 두번째 멤버로 튜플을 가지는 구조
const trios: [string, [number, boolean]][] = [
	["Amanitore", [1, true]],
	["AEthelflaed", [2, false]],
	["Ann E. Dunwoody", [3, false]],
];

//각 ...trio가 logTrio의 매개변수 타입과 일치하므로 통과
trios.forEach(trio => logTrio(...trio)); //Ok

//첫 번째 매개변수로 타입이 string인 [string, [number, boolean]] 전체를 전달하려고 시도하기에
//할당 X 
trios.forEach(logTrio);
//Error: Argument of type '(name: string, value: [number, boolean]) => void' is not 
// assignable to parameter of type '(value: [string, [number, boolean]], ...) => void'.
// Types of parameters 'name' and 'value' are incompatible
// Type '[string, [number, boolean]]' is not assignable to type 'string'.

6.4.2 튜플 추론

  • 타입스크립트는 생성된 배열을 튜플이 아닌 가변 길이의 배열로 취급

  • 배열의 변수의 초깃값 또는 함수에 대한 반환값으로 사용될 경우, 유연한 크기의 튜플 배열로 가정한다.

  • 타입스크립트에서는 값이 일반적인 배열 타입 대신 구체적인 튜플 타입이어야 함을 나타낸다

    • 명시적 튜플 타입

    • const 어서션

      // [string, number]가 아니라 (string | number)[]를 반환하는 것으로 유추
      // 반환 타입: (string | number)[]
      function firstCharAndSize(input: string) {
      	return [input[0], input.length];
      }
      
      // firstChar 타입: string | number
      // size 타입: string | number
      const [firstChar, size] = firstCharAndSize("Gudit");
      ​
      
  • 명시적 튜플 타입

    • 튜플 타입도 타입 애너테이션에 사용할 수 있다.

    • 함수가 튜플 타입을 반환하다고 선언 및 배열 리터럴을 반환한다면 해당 배열 리터럴은 일반적인 가변길이의 배열 대신 튜플로 간주된다.

      //반환 타입: [string, number]
      function firstCharAndSizeExplicit(input: string): [string, number] {
      	return [input[0], input.length];
      }
      
      // firstChar 타입: string
      // size 타입: number
      const [firstChar, size] = firstCharAndSizeExplicit("Cathy Williams");
  • const 어서션

    • 타입스크립트는 값 뒤에 넣을 수 있는 const 어서션인 as const 연산자를 제공

    • const 어서션은 타입스크립트에 타입을 유추할 때 read-only가 가능한 값 형식을 사용하도록 지시

      //타입: (string | number)[]
      const unionArray = [1157, "Tomoe"];
      
      //타입: readonly [1157, "Tomoe"]
      //배열이 튜플로 처리되어야 함을 나타낸다.
      const readonlyTuple = [1157, "Tomoe"] as const; 
    • 유연한 크기의 배열을 고정된 크기의 튜플을 전환하는 것 이외 해당 튜플이 read-only이며 값 수정이 예상되는 곳에서는 사용될 수 없을을 나타낸다.

      //pairMutable은 명시적 튜플 타입이어서 수정될 수 있다.
      //하지만 as const는 값이 변경될 수 있는 pairAlsoMutable에 할당할 수 없도록 하며,
      //상수 pairConst의 멤버는 수정을 허용하지 않는다.
      const pairMutable: [number, string] = [1157, "Tomoe"];
      pariMutable[0] = 1247; Ok
      
      const pairAlsoMutable: [number, string] = [1157, "Tomoe"] as const;
      //Error: The type 'readonly [1157, "Tomoe"]' is 'readonly'
      // and cannot be assigned to the mutable type '[number, string]'.
      
      const pairConst = [1157, "Tomoe"] as const;
      pairConst[0] = 1247;
      //Error: Cannot assign to '0' because it is a read-only property.
    • 튜플을 반환하는 함수로부터 반환된 값은 보통 즉시 구조화되지 않기에 read-only 전용인 튜플은 함수를 사용하는데 방해가 되지 않는다.

      //반환타입: readonly [string, number]
      //읽기 전용 [string, number]를 반환하지만, 해당 함수를 사용하는 코드에서는 해당 튜플에서 값을
      //찾는것에만 관심을 둔다.
      function firstCharAndSizeAsConst(input: string) {
      	return [input[0], input.length] as const;
      }
      
      //firstChart 타입: string
      //size타입 : number
      const [firstChar, size] = firstCharAndSizeAsConst("Ching Shih");

Share article

Typescript Study