TypeScript 7
0. 배열
내부에 모든 타입의 값을 혼합해서 저장이 가능했던 자바스크립트 배열과는 달리, 타입스크립트는 하나의 배열이 특정 데이터 타입으로만 동작하도록 제한한다.
1. 배열 타입
배열에 초깃값을 넣으면, 자동으로 해당 배열의 요소의 타입을 유추할 수 있다.
그러나 다음과 같은 배열에 대한 타입 애너테이션을 통해 배열의 요소 타입이 무엇인지 명시할 수도 있다.
let arrayOfNumbers : number [];
arrayOfNumbers = [1,2,3,4,5];
1.1 배열과 함수 타입
let createStrings: () => string [];
let stringCreators: (()=> string)[];
위의 예시에서 createStrings는 string 배열을 반환하는 함수이고, stringCreators는 string을 반환하는 함수의 배열이다.
치어럼 괄호를 통해 애너테이션의 어느 부분이 함수 반환 부분이고 어느 부분이 배열 타입 묶음인지를 잘 명시해야 한다.
1.2 유니언 타입 배열
배열의 각 요소가 여러 타입 중 하나일 수 있을 때는 유니언 타입을 사용한다.
let stringOrArrayOfNumbers: string | number [];
let arrayOfStringOrNumbers: (string|number)[];
유니언 타입을 배열 타입 애너테이션과 함께 사용할 때는 어느 부분이 배열의 컨텐츠이고 어느 부분이 유니언 타입 묶음인지를 나타내기 위해서 위와 같이 괄호를 사용해야 할 수도 있다.
stringOrArrayOfNumbers는 해당 타입이 string 혹은 number의 배열일 수 있는 유니언 타입 변수이고,
arrayOfStringOrNumbers는 배열의 요소가 string 혹은 number임을 나타낸다.
1.3 any 배열의 진화
초기에 초깃값이 설정되지 않거나, 타입 애너테이션이 붙어있지 않은 빈 배열의 경우, 타입스크립트는 이를 any[]로 취급하고 모든 타입의 값을 받을 수 있다.
let values = []; //타입 : any[]
values.push(''); // 타입 : string[]
values[0] = 0; // 타입 : (string | number) []
위와 같이 어떤 타입의 값이든 받을 수 있기 때문에, 잠재적으로 잘못된 값 추가를 허용할 수 있어, 주의해야 한다.
1.4 다차원 배열
let arrayOfArraysOfNumbers: number[][];
arrayOfArraysOfNumbers = [
[1,2,3],
[2,3,4],
[3,4,5],
];
위와 같은 2차원 이상의 배열의 경우, 해당 타입의 배열을 요소로 가지는 배열로 해석할 수 있으며, 이는 결과적으로 다음 예시의 두 변수의 타입이 동일함을 나타낸다.
let test: number[][];
let test2: (number[])[];
2. 배열 멤버
타입스크립트는 배열의 멤버를 찾아서 해당 배열의 타입 요소를 반환하는 전형적인 인덱스 기반 접근를 지원하는 언어이다.
const testStr = ["test1","test2"];
const testString = testStr[0] // type : string
const strOrDates = ["test",new Date(2023,8,15)];
const ret = strOrDates[0]; // 타입 : string | Date
해당 배열의 타입 요소를 그대로 반환하기 때문에, 위의 strOrDates와 같이 유니언 타입 배열의 경우, 그 반환값 역시 유니언 타입이다.
2.1 불안정한 멤버
function withElements(elements : string[]) {
console.log(elements[9001].length); // 타입 오류가 없음
}
withElements(["test","test2"]);
자바스크립트의 경우 배열의 길이보다 큰 인덱스로 접근하면 undefined로 인식한다.
그러나 타입스크립트에서 위와 같은 사항일 경우, 코드 스니펫에서 elements[9001]은 undefined가 아닌 string 타입으로 간주된다.
이처럼 타입스크립트는 검색된 배열의 멤버가 존재하는지 등을 확인하지 않는 경우가 있으므로, 배열의 길이 등을 체크하는 코드가 필요하다.
cf. noUncheckedIndexedAccess 등과 같은 배열 조회를 더 제한하고 타입을 안전하게 만드는 플래그가 있으나, 너무 엄격해서 대부분의 프로젝트에서는 사용하지 않는다.
3. 스프레드
… 스프레드 연산자를 사용해 배열을 결합할 수 있다.
const str = ["test","test2"];
const nums = [10,20,30];
const conjoined = [...str, ...nums];
// type : (string|number)[]
console.log(conjoined[0]) // 출력값 : test
입력한 배열이 동일한 타입이라면 출력 배열도 동일한 타입이다.
그러나, 서로 다른 타입의 두 배열을 스프레드해 새로운 배열을 생성 시, 새로운 배열은 유니언 타입 배열이 된다.
4. 튜플
튜플 배열은 각 인덱스에 특정 타입을 가지면, 배열의 모든 가능한 멤버를 갖는 유니언 타입보다 더 구체적이다.
let numberAndStr: [number,string];
numberAndStr = [530, "test"]; // OK
numberAndStr = [false,"testt"] // Error
numberAndStr = ["test"] // Error
다음과 같은 형태의 튜플 배열도 선언이 가능하다.
const test: [string,[number,boolean]][] =[
["test",[1,true]],
["test2",[2,false]],
]
4.1 튜플의 할당 가능성
튜플 타입은 가변길이의 배열 타입보다 더 구체적으로, 가변 길이의 배열 타입은 튜플 타입에 할당이 불가능하다.
const test = [false, 123]; //타입 :(boolean | number) [];
const testVar : [boolean, number] = test //Error : 가변길이의 배열 타입은 튜플 타입에 할당이 불가능
나머지 매개변수로서의 튜플
튜플은 구체적인 길이와 요소 타입 정보를 가지는 배열로 간주된다.
따라서 함수에 전달한 인수를 저장하는 등에 유용하게 사용될 수 있다.
function logPair(name: string , value: number) {
console.log(`${name} has ${value}`);
}
const pairArray = ["JDM",1];
logPair(...pairArray) // Error
const pairArrayError : [number, string] = [ 1,"JDM"];
logPair(...pairArrayError) // Error
const pairArrayCorrect : [string, number] = ["JDM", 1];
logPair(...pairArrayCorrect); // OK
위의 경우와 같이 타입스크립트는 … 나머지 매개변수로 전달된 튜플에 정확한 타입 검사를 제공할 수 있다.
이를 다음과도 같이 응용 가능하다.
function printTest(name: string, value: [number,boolean]){
console.log(`${name} has ${value[0]}, ${value[1]}`);
}
const test: [string,[number,boolean]][] =[
["test",[1,true]],
["test2",[2,false]],
]
test.forEach(t => printTest(...t)); //OK
4.2 튜플 추론
타입스크립트는 명시적으로 튜플임을 나타내지 않는 이상, 생성된 배열을 튜플이 아닌 가변 길이 배열로 취급한다.
타입스크립트에서는 가변 길이 배열이 아닌 좀 더 구체적인 튜플 타입이어야 함을 명시적 튜플 타입과 const 어서션을 사용해 나타낸다.
명시적 튜플 타입
function test(input: string): [string,number]{
return [input[0],input.length];
}
const [testVar, size] = test("testStr");
// testVar 타입 : string
// size 타입 : number
함수가 튜플 타입을 반환한다고 선언되고, 배열 리터럴을 반환 시, 해당 배열 리터럴은 일반적인 가변 길이의 배열이 아닌 튜플로 간주된다.
const 어서션
타입스크립트는 값 뒤에 넣을 수 있는 const 어서션인 as const 연산자를 제공한다. const 어서션은 타입스크립트가 타입을 유추할 때, 읽기 전용 형식을 사용하도록 지시한다.
const unionArray = [11,"test"];
const readOnlyTuple = [11,"test"] as const
위와 같이 배열 리터럴 뒤에 as const가 배치되면 배열이 튜플로 처리되어야 함을 나타낸다.
그러나 이러한 as const를 사용하면, 해당 배열을 고정된 크기의 튜플로 전환하는 것을 넘어, 읽기 전용이 되고, 값 수정이 불가능하게 된다.
(return 문의 마지막에 as const를 사용시 반환된 값을 받는 변수도 const 값으로 수정이 불가능)