안녕하세요.
Hynn 입니다.
이번 포스팅에서는 연속적으로, 원시타입부터, TypeScript 에서 사용할 수 있는 타입의 종류, 작성방법에 대해서 작성해보도록 하겠습니다.
JavaScript 에서는 변수에 담기는 컨텐츠들이 각자 고유의 타입을 가지고 있습니다.
기존에서 JavaScript 의 타입은, 사용자가 추측하여 작성하거나, 어떤 타입이 되었든, 형변환을 통해 사용하는 형태로 사용했다면, TypeScript 에서는 코드 작성자가 타입을 명시해야 하며, 이를 준수하지 않을 경우, 오류가 반환되는 형태로 작성이 필요합니다.
시작해보겠습니다.
==============
1. 타입에 대해 알아보기
2. 원시타입 &
3. 유니언타입
4. 타입 작성방법
==============
1. 타입에 대해 알아보기
JavaScript 에서는 Type 을 확인할 수 있는 아주 간단한 방법이 있습니다. 바로 "Typeof" method 를 사용하여 변수나 값이 어떤 타입을 가지고 있는지를 알 수 있습니다.
예시를 통해 확인하려면 아래와 같이 작성해볼 수 있습니다.
const test = () => {
console.log("test");
};
const test2 = 10n;
console.log(typeof test);
console.log(typeof test2);
기존의 JavaScript 는 Runtime, 즉 코드를 "실행" 해야 결과를 알 수 있고, 오류가 발생하더라도, 실행한 직후에 오류가 발생됩니다.
하지만 TypeScript 는 이름 그대로, 코드를 작성하는 단계부터 Type 을 명시할 수 있고, 명시한 Type 과 일치하지 않으면, 불일치로 인한 오류가 코드 작성단계부터 발생하게 됩니다.
또한 이 타입을 사용하면, 코드의 작성 효율을 올릴 수 있습니다.
먼저 이를 이용해 간단한 예시를 살펴보면 아래와 같습니다.
각각 JavaScript 와 TypeScript 에서 변수에 Number 라는 타입을 담고, " . " 을 사용하면 나타나는 method 입니다.
이는 매우 단순한 원시타입이기때문에, IDE(Visual Studio Code) 에서 "타입 추론" 을 해서, 두 가지가 큰 차이가 없게 나타나지만, 추론이 되지 않는 단계에서는 매우 유의미한 차이가 있습니다.
바로, 내가 지정한 타입에서 어떠한 Method 를 사용할 수 있는지를 간결하고, 확실하게 알 수 있습니다.
즉, TypeScript 에서 작성한 코드에서는, 작성단계에서부터, 명시된 타입에 따른 사용가능한 메서드가 나열되고, 이 메서드를 사용해 오류가 발생한 것이라면, "타입"이 올바르지 않게 반환되었거나, 변수/메서드의 값이 작성자가 명시한 타입이 아니라는 의미로 전달이 되기도 합니다.
따라서, TypeScript 에서 타입은 기초적으로 숙지해야 할 매우 중요한 요소라고 할 수 있습니다.
이 타입은, 원시타입을 비롯해서, 여러가지 타입이 있습니다.
이번 포스팅에서는 가장 기본적으로 사용되는 원시타입을 알아보고, 타입별로 어떠한 의미인지를 알아보도록 하겠습니다.
2. 원시타입
원시타입은, JavaScript 에서 말하는 원시타입과 거의 동일합니다.
이 원시타입은 가장 기초적인 타입으로, 여기에서는 원시타입과 더블어, 범용적 타입까지 같이 알아보도록 하겠습니다.
1) Number, String, Boolen
위 세개의 타입은, 우리가 잘 아는 기초적인 타입입니다.
각각 아래의 의미를 가지고 있습니다.
타입명 | 설명 | 예시 |
Number | 일반적으로 JavaScript 에서 사용하는 숫자입니다. 정수 및 소수점 숫자를 포함합니다. | const number = 10 |
String | 단어 그대로 문자열로 이루어진 타입입니다. 일반적으로 '' 으로 감싸서 표현합니다. | const string = 'text' |
Boolean | 참 또는 거짓의 타입이며, true or false 로 표현합니다. | const boolean = true |
가장 기초적인 타입으로써, JavaScript 에서도 많이 사용해본 타입일 것이라고 생각합니다.
타입에 따라, 사용가능한 method 가 존재하기때문에, TypeScript 는 이러한 타입이 명시가 되면, 사용가능한 method 를 자동으로 처리해줍니다.
이전 포스팅에서 언급한 개념이기도 한 "타입 추론" 기능을 효과적으로 사용하기 위해서는, 사소한 타입이더라도 명시적으로 표현을 해야, 타입 추론 기능의 장점을 잘 활용할 수 있습니다.
2) unkown, any
이 두개의 타입은 가장 주의해서 사용해야 하는 타입입니다.
특히나, "Any" 의 경우, 사실상 없다고 생각하고 사용해야 합니다.
이유는 아래와 같은 차이가 존재하기 때문입니다.
타입명 | 설명 | 예시 |
Any | 값에 대해 타입 정보가 없음을 나타냅니다. 이 경우 어떠한 타입의 값, 속성에도 사용이 가능합니다. 하지만 이 타입의 경우, TypeScript 에서 가장 중요한 장점 중 하나인 타입 검사를 무시(생략)함으로, TypeScript 를 사용하는 이유가 없습니다. | const any = any |
Unknown | Any 를 대체해서 사용하지만, 역시 타입 정보가 없음을 나타냅니다. 하지만 Any 와 다르게 안전한 타입 검사를 위해 사용됩니다. 즉, 일반적으로 Return 되는 값을 알지 못하고, 확인이 필요한 경우, unknown 으로 타입검사를 한 뒤, 올바른 타입을 명시할 수 있습니다. | const unknown = unknown |
즉, 외부 라이브러리, 혹은 API 등을 사용할 때는, 타입을 예상할 수 없거나, 응답이 어떻게 오는지를 확인 후 타입을 지정해야 하는 경우가 발생합니다. 이 때는, Unknwon 을 사용해 Return 되는 값의 타입을 확인 후, 그에 맞게 수정을 해야 하는 작업을 거쳐야 합니다.
Any 와는 다르게, Unknwon 은 타입검사를 진행하는 것이 가장 큰 차이가 있습니다.
처음에 타입의 대한 난이도때문에 "Any" 를 난사한다면, Typescript 를 안쓰는게 좋다고 할 수 있습니다.
3) Null, undefined, void
이 3개의 타입은, 값이 특정상황일 경우 사용하는 타입입니다.
각각 유사한 의미를 가질수도 있겠지만, 타입별로 정의하는 바가 분명하게 다르기 때문에, 이를 구분해야 합니다.
타입명 | 설명 | 예시 |
Null | 값이 없는 타입을 뜻합니다. Undefined 와는 다르며, 리턴한 값이 "null" 이라는 의미로 처리됩니다. 즉, 리턴은 되었지만, 값이 비어있는 타입을 정의할 때 사용합니다. | const null = null |
Undefined | 값이 "할당"되지 않은 변수를 뜻합니다. 즉 빈 값이 아니라, 할당자체가 되지 않은 변수로, Null 과는 다른 값을 정의합니다. |
const undefined = undefined |
Void | Null 과 undefined 와는 다르게, 반환되는 함수의 값이 없을 때 사용합니다. 주로 Void 의 사용은 console.log 와 같이 함수의 반환값이 없는 경우에 사용됩니다. | const void = console.log('void') |
즉 일반적으로 값 자체의 경우, null 이나 undefined 를 사용하지만, 반환의 값이 결정되는 것이 함수이고, 콘솔로그와 같이 값이 반환되지 않는 함수의 결과라면 Void 를 명시해야 합니다.
이 두가지의 차이를 이해한다면 쉽게 이를 적용할 수 있습니다.
4) Symbol
객체의 속성을 정의하는 Symbol 은 ES6 부터 도입된 원시 데이터입니다.
이 속성은 주로, 객체 속성의 "Key" 로 사용되는 데이터 타입입니다. 이 타입은 주로 충돌을 방지하기 위해 사용됩니다.
여기서부터는 작성예제와 함께 간단하게 참고해보도록 하겠습니다.
타입명 | 설명 | 예시 |
Symbol | Symbol 은 객체속성의 키로 사용됩니다. 주로 객체의 충돌을 방지하기 위해 사용하고, 이름이 같은 다른 속성과 식별자가 겹치지 않도록 부여하는 타입입니다. |
const symbol1 = Symbol("Symbol") const symbol2 = Symbol("Symbol") console.log(symbol1 === symbol2) // false |
위와 같이, 각자의 Symbol 은 고유한 값으로서, 객체 속성에 키로 사용됩니다. 각각의 Symbol 은 고유한 값으로 타입이 명시되며, 이를 사용해 속성 이름 충돌을 방지할 수 있습니다.
이를 추가적으로 사용하면, "Symbol.for(key)" 를 사용하면, 주어진 키와 동일한 Symbol 이 이미 존재한다면, 동일한 Symbol 로 반환하거나, 새로운 Symbol 을 사용할 수도 있습니다.
const sharedSymbol1 = Symbol.for("sharedKey");
const sharedSymbol2 = Symbol.for("sharedKey");
console.log(sharedSymbol1 === sharedSymbol2); // true
5) bigint
일반적으로 잘 사용할 타입은 아니지만, 정수의 숫자 중 큰 숫자를 지정할 때 사용합니다.
이 bigint 는 ES2020 이후에만 사용이 가능하므로, 이전 버전에서는 사용할 수 없습니다.
주로 아래와 같이 표현합니다.
const bigint = 10n
위의 예시에서는 10의 n승이므로, 일반적인 number 타입에는 담을 수 없는 큰 숫자를 나타냅니다.
3. 유니언 타입(Union-Type)
때때로 타입은 Number 혹은 String 과 같이, 상황에 따라서 다른 값을 반환하는 상황이 발생할 수 있습니다.
이러한 경우 사용하는 타입이 유니언타입입니다. 유니언 타입은 타입을 2개이상을 지정할 수 있습니다.
즉 개수의 한계는 없지만, 이는 논리적으로 코드의 가독성측면이나, 복잡성 측면에서 좋지 않습니다.
이러한 점을 무시하고 극단적으로 표현한다면 아래와 같이도 사용이 가능합니다.
하지만 이렇게 모든 타입을 나열할 경우, Any 나 Unknwon 과 다를바가 없기 때문에, 논리적으로 정해진 유형이 아니라면 사용하지 않는 것이 좋다고 할 수 있습니다.
1) 잘못된 예제
type ComplexUnionType = string | number | boolean | null | undefined | object
2) 올바른 예제
type ComplexUnionType = string | number
위의 타입의 예제를 보면, 잘못된 예제에서는 어떠한 타입이든 유니언타입에 담아 타입추론/검사를 통과하는게 목적이라면, 올바른 예제에서는 논리적으로 예상되는 2개의 선택지에서 사용되었습니다.
잘못된 예제로 작성한 것은, 사실 unknown 을 사용하여, 반환되는 타입을 확인 후 그를 올바르게 업데이트 하는게 좋다고 할 수 있습니다.
4. 타입 작성방법
타입을 작성하는 기본 방법은 변수명에 타입을 명시해야 합니다.
각 예제를 아래와 같이 작성할 수 있습니다.
const number: number = 1;
const string: string = "string";
const boolean: boolean = true;
const Null: null = null;
const Undefined: undefined = undefined;
const Any: any = null;
const Void: void = console.log("1");
const symbol: symbol = Symbol("1");
추가적으로, 함수역시도 타입을 명시할 수 있습니다.
여기서는 원시타입만을 가지고 함수를 몇가지 예제를 작성해보겠습니다.
function number(): number {
return 10;
}
const num = (): number => {
return 10;
};
각각 선언형 함수, 화살표함수를 사용한 작성 예제입니다.
함수의 타입선언은 매개변수가 들어가는 괄호 " () " 내에 지정해야 합니다.
이를 응용하면, 괄호 내에 매개변수의 타입 역시 지정이 가능합니다
function number(Num1: number, Num2: number): number {
return Num1 + Num2;
}
const num = (Num2: number, Num3: String): number => {
return Num2 + Num3;
};
하지만 두번째 애로우함수처럼, 함수의 로직에서, Num2+ Num3 를 더하도록 Return 을 작성했지만, Num3 가 String 과 같은 경우에는 논리적으로 올바르지 않은 작성이므로, 오류를 반환합니다. 이러한 부분 역시 타입스크립트에서 타입을 명시하면, 런타임 이전에 코드의 유효성을 올바르게 검증합니다.
위에서 올바르게 동작하도록 변경하려면 Num2 와 Num3 의 타입을 수정하거나, toString().length 와 같이 문자열을 원하는 형태로 변환하여 사용할 수도 있습니다.
1) 이제 대표적으로 사용하는 Try & Catch 문을 알아보도록 하겠습니다.
const BlogController = (num1: number, num2: number): number => {
try {
const result = num1 + num2;
return result;
} catch (e) {
console.log(e);
throw new Error(e);
}
};
위의 예제를 본다면, 각 매개변수에 타입을 명시하고, 함수의 반환타입을 명시했습니다.
또한 catch 에서는 console.log 역시 출력을 하지만, Error 를 발생시키므로, 별도의 명시 없이도, 명확한 코드 작성이 가능합니다.
이렇게 작성하면, TypeScript 에서 작성자가 명시한 타입에 맞게 올바른 작성여부를 실행하지 않아도 확인할 수 있습니다.
즉, 매개변수에 각각에 인수(인자)로 들어가는 값에 대한 타입, 그리고 난 결과에 대한 타입을 지정하여 사용할 수 있습니다.
일반적으로 Try & catch 문에서 "Throw new Error" 는 return 값으로 보기 때문에, void 와 같은 처리를 생략해도 됩니다.
2) Closure 함수
클로저 함수는 각각에 타입을 지정하는 형태로 사용이 가능합니다.
예제를 작성해보면 다음과 같습니다.
const createNumber = (x: number): ((y: number) => number) => {
return (y: number): number => {
return x + y;
};
};
각각 내부함수, 외부 함수의 변수에 접근해야 하는 클로저 함수의 특성상, 각각의 매개변수, 결과값에 타입을 선언하고, 이 값을 참조하도록 작성이 되야합니다.
따라서 각 매개변수의 인자에 대한 타입과 결과에 대한 타입을 명시하면 됩니다.
위의 예제들은 각각 모두 기본적인 원시타입을 사용한 예제입니다.
다음 포스팅에서는 이를 보다 구체화하여 사용할 수 있는 인터페이스에 대해서 다루어보도록 하겠습니다.
감사합니다.
댓글