![[Typescript] 배열(2)](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZpLBP%2FbtsIZtlv0ZL%2FPk4HcUtI2BAlTxp7YiKFAK%2Fimg.png)
4️⃣ 튜플
튜플은 고정된 크기의 배열이다.
튜플 배열은 각 인덱스에 특정 타입을 가져야 하며 선언을 할때에는 요소의 타입을 적어 선언한다.
let yearAndWarrior: [number, string];
yearAndWarrior = [530, "Tomyris"]; /*OK*/
yearAndWarrior = ["530","Tomyris"]; /*Error: Type string 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
*/
JavaScript에서 조건 하나를 가지고 두 개의 변수에 initiate value를 설정하는 것처럼 한 번에 여러 값을 할당하기 위해서 튜플과 배열 구조 분해 할당을 함께 사용한다.
/*
Type : year == number
Type : warrior == string
*/
let [year,warrior] = Math.random() > 0.5
? [340,"Archidamia"]
: [1028, "Rani of Jhansi"]
console.log(year, warrior);
4️⃣ - 1. 튜플 할당 가능성
튜플 타입은 가변 길이의 배열 타입보다 더 구체적으로 처리된다.
즉, 가변 길이의 배열 타입은 튜플 타입에 할당할 수 없다.
아래의 코드를 보자.
/* Type : (boolean | number)[]*/
const pairLoose = [false,123]; /*배열*/
const pariTupleLoose : [boolean, number] = pairLoose; /*튜플 : error*/
/*
Error: Type (number | boolean)[] is not assignable to type [boolean, number]
Target requires 2 element(s) but source may have fewer.
*/
에러 메시지는 pairLoose (배열)타입이 pairTupleLoose (튜플)타입에 할당될 수 없음을 알려준다.
그 이유는 두가지이고 아래와 같다.
배열 (boolean, number)[ ] | 튜플 [boolean, number] | |
요소의 순서와 타입 고정 | X | O |
길이의 불확실성 | 길이가 가변적 | 길이가 불변 |
이러한 이유로 배열 → 튜플의 할당은 불가능하다!
나머지 매개변수로서의 튜플
튜플은 구체적인 길이와 요소 타입 정보를 가진다.
즉, 함수에 전달할 인수를 저장하는데 굉장히 유용하다.
아래의 코드를 보자.
function logPair(name:string, value:number){
console.log(name + ': ' + value + '\n');
}
/*Case 1*/
const pairArray = ["kim",25];
logPair(...pairArray);
/*
Error: A spread argument must either have a tuple type or be passed to a rest parameter.
*/
/*Case 2*/
const pairTupleIncorrect : [number,string] = [1,"Amage"];
logPair(...pairTupleIncorrect );
/*
Error: Argument of type number is not assignable to parameter of type string
*/
/*Case 3*/
const pairTupleCorrect :[string,number] = ["kim",25];
logPair(...pairTupleCorrect ); /* OK */
logPair함수에 인수를 전달하고 있는데 TypeScript는 이렇게 ...나머지 매개변수로 전달된 튜플에 정확한 타입 검사를 제공할 수 있다.
Case1을 보면 pairArray는 (string | number)[ ] 타입의 값을 가지는데 이 값을 함수에 인수로 전달하려고 했을 때 값의 타입이 동일하거나 잘못된 순서로 정해진채 함수에게 전달될 수 있다.
const incorrectPairArray1 = [25,"kim"];
const incorrectPairArray2 = ["kim","son"];
이는 타입의 안전을 보장하지 못한다!
나머지 매개변수 튜플을 사용하고 싶다면 여러 번 함수를 호출하는 인수 목록을 배열에 저장해 함께 사용할 수 있다.
아래의 예시를 보자.
- trios : 튜플 배열
- trios의 각 튜플은 두 번째 멤버로 또 튜플 ([number, boolean]) 을 가진다.
function logTrio(name: string,value: [number, boolean]){
console.log(`${name}: ${value[0]} , ${value[1]}`);
}
const trios : [string, [number,boolean]][] = [ /*여러번 함수를 호출하는 인수목록*/
["Indigochild",[1,true]],
["KimMinJae",[15,false]],
["Son",[7,true]]
];
trios.forEach(trio => logTrio(...trio)); /* OK */
trios.forEach(logTrio);
/*
Error: Argument of type
(name: string, value: [number, boolean]) => void
is not assignable to parameter of type (value: [string, [number, boolean]], index: number, array: [string, [number, boolean]][]) => void
Types of parameters name and value are incompatible.
Type [string, [number, boolean]] is not assignable to type string
*/
4️⃣ - 2. 튜플 추론
TypeScript는 생성된 배열을 튜플이 아닌 가변 길이의 배열로 취급한다.
배열이 변수의 ①초기값 또는 ②함수에 대한 반환값으로 사용될 경우에
❌고정된 크기의 튜플이 아니라
⭕유연한 크기의 배열로 취급한다.
/* Return Type: (string | number)[] */
function firstCharAndLength(input : string) {
return [input[0],input.length];
}
/*
firstChar Type : string | number
size Type : string | number
*/
const [firstChar, size] = firstCharAndLength("Hello World!");
함수 firstCharAndLength의 반환값의 타입은 [string, number] 이다.
따라서 반환 타입은 (string | number)[ ]를 반환하는 것으로 유추된다.
TypeScript에서는 값이 일반적인 배열(문자열)을 지양하고 구체적인 튜플 타입이어야 함을 지향하며 아래의 두 가지 방법으로 나타낸다.
- 명시적 튜플 타입
- const 어서션(assertion)
명시적 튜플 타입
튜플 타입도 타입 에너테이션에 사용할 수 있다.
아래의 코드와 같이 함수가 튜플타입을 반환한다고 선언하면➡️함수는 반환 배열을 가변 길이의 배열이 아닌 튜플로 간주한다.
/* Return Type: [string, number] */
function firstCharAndLength(input : string) : [string,number] {
return [input[0],input.length];
}
/*
firstChar Type : string
size Type : number
*/
const [firstChar, size] = firstCharAndLength("Hello World!");
const 어서션(assertion)
명시적 튜플 타입 선언은 강력하지만 개발자 입장에선 일일이 작성하기엔 너무 귀찮다.
그래서 TypeScript는 ⭐const 어서션인 as const 연산자를 제공⭐한다.
1. const 어서션은 타입을 유추할 때 읽기 전용(read-only)이 가능한 값 형식을 사용하도록 지시한다!
2. const 어서션은 배열 to 튜플의 전환을 넘어서 해당 튜플이 읽기 전용이고 값을 수정할 수 없음을 나타낸다!
/* Type: (string | number)[] */
const unionArray = [2024,"DragonBelt"];
/* Type: readOnly [2024,"DragonBelt"]*/
const readOnlyTuple = [2024,"DragonBelt"] as const;
unionArray[0] = 2025; /* OK */
readOnlyTuple[0] = 2025;
/*
Error: Cannot assign to 0 because it is a read-only property.
*/
const pairAlsoMutable : [number,string] = [2024,"DragonBelt"] as const;
근데 이제 pairAlsoMutable을 보면...
I think...
pairAlsoMutable: [number, string]이라는 타입 선언은 [number, string] 형식의 튜플을 나타낸다.
이 튜플은 mutable하며(as const로 선언하지 않았기 때문) 요소들은 각각 number와 string 타입을 가지면 된다.
허나, 우변의 "as const"는 이 배열을 [2024, "DragonBelt"]와 같은 리터럴 타입으로 고정시키기 때문에 이 값을 [number, string]타입의 변수에 할당할 수 없으며 오류가 발생할 것이라고 생각했다.
Actually...
"as const"를 사용하면 배열 [2024, "DragonBelt"]는 readonly [2024, "DragonBelt"] 타입을 가지게 된다.
여기서 readonly는 배열과 내부 요소들의 불변을 나타내지만 이 타입은 여전히 [number, string]과 호환되고 있었다..(에러가 나지 않았다는 것)
자세히 알아보니 TypeScript는 특정 리터럴 타입(readonly[2024, DragonBelt])을 더 일반적인 타입([number, string])으로 할당하는 것을 허용한다고 한다.
이를 타입 호환성의 작동에서 알 수 있었는데 ①하위타입(subtype)과 ②할당(assignment)이라는 두 가지 호환성 개념이 있었다.
실질적으로 할당 호환성이 더 일반적인 호환성 규칙을 제공한다고 한다.
할당 호환성이란 ⭐구체적 타입➡️일반적 타입으로 할당되는 것을 허용하는 방식⭐을 의미함.
Documentation - Type Compatibility
How type-checking works in TypeScript
www.typescriptlang.org
'FrontEnd > Typescript' 카테고리의 다른 글
[Typescript] 인터페이스(2) (0) | 2024.08.19 |
---|---|
[Typescript] 인터페이스(1) (0) | 2024.08.19 |
[Typescript] 배열(1) (0) | 2024.08.05 |
[Typescript] 함수(2) (0) | 2024.03.06 |
[Typescript] 함수(1) (1) | 2024.03.05 |
안녕하세요? 개발자입니다.