본문 바로가기
Angular/Angular Developer Roadmap

Learn TypeScript Basics 기초 타입스크립트

by chief_sac 2021. 10. 20.
반응형

Angular Framework 이해를 위해 공부한 내용 이다

참고자료는

위와 같다. 위 내용들을 이해하면서 하나씩 짚어가는데에 큰 시간이 걸렸기에 조금 내스타일대로 이해 쉽게 정리해보았다.

 

목차

 

  • What is TypeScript ( 타입스크립트란?)
  • Why use TypeScript ( 왜 타입스크립트를 사용하는가?)
  • Structural Typing ( 구조적인 타이핑)
  • Type Inference ( 유형 추론)
  • Type Inference ( 유형 추론)
  • Union Types (유니온 타입)
  • Built-in types (내장된 타입)
  • Type Guards ( 타입가드)

 

 

What is TypeScript ( 타입스크립트란?)


 

타입스크립트는 자바스크립트의 태생적 문제를 극복하고자 CoffeeScript, Dart, Haxe와 같은 AltJS로서 등장하였다. 또한 자바스크립트의 Superset(상위확장)이다.

  • 자바스크립트의 태생적인 문제
    • 자바스크립트는 웹의 어셈블리어같은 중요한 언어지만 완전한 보안이 되기전 출시된 문제와 과거에 웹페이지(HTML)의 보조적인 기능을 수행하기위한 목적으로 만들어져 태생적인 한계를 가지고 있다.
    • 다른 언어와의 특별히 구별되는 특징으로는
      • 다른 클래스기반 객체지향 프로그래밍과는 다르게 자바스크립트는 프로토 타입 기반 객체지향 프로그래밍 언어이다.(ECMAScript6 부터는 클래스가 추가되긴함)
      • 스코프와 this
      • 동적 타입(dynamic typed)언어 와 느슨한 타입(loosely typed) 언어로 자바스크립트는 동적타입언어이다. 말그대로 변수의 타입지정 없이 값이 할당되는 자유롭게 사용가능하다.
  • 타입 스크립트는 정적인 타이핑을 지원하고, 클래스와 , Decorator등을 지원한다.

 

 

 

 

Why use TypeScript ( 왜 타입스크립트를 사용하는가?)


 

장점

  • 정적 타입 지원
    • 정적 타입 언어에서는 하위 시스템 사이간의 경계가 인터페이스를 통해서 정의되는데 일반적인 JS의 경우 인터페이스가 없어 순수한 JS만으로는 경계를 만드는데의 한계가 있다. 이경우 추상화가 아니라 구상 타입에 의존하게되고 이는 커플링을 발생시켜 스파게티 코드를 만드는 주범이 되기도 한다.

 

function sum(num1: number, num2: number){
	return a + b;
}

sum('x','y');

위와같이 타입 스크립트는 정적 타입을 지원하여서 컴파일 단계에서 오류를 잡고 개발자의 의도대로 코드를 작성할 수 있습니다. 이는 코드의 가독성을 높이고 오류를 예측 하는 행위에서의 장점이 있습니다.

 

  • Tool 지원

자바 스크립트의 경우 인텔리센스와 코드어시스트와 같은 지원들을 받을 수 없어 불편한 경우가 많았지만 타입스크립트의 경우에는 IDE같은 도구에 타입 정보를 제공함으로서 위와 같은 지원들을 받을수 있어 규모가 큰 프로젝트를 진행함에 있어 큰 이점이 될 수 있다.

 

 

  • 객체지향 프로그래밍 지원

객체지향중 인터페이스, 제네릭, 클래스와 같은 강력한 객체지향을 지원하고 이는 복잡한 코드를 쉽게, 반복하지 않고 구성을 할수 있으며 기존 Java와 같은 클래스 기반의 객체지향 언어를 사용하던 개발자에게 상대적으로 낮은 진입장벽을 제공합니다.

 

 

Structural Typing ( 구조적인 타이핑)


 

Structural Type System 또는 Duck Typing이라고 불리는 구조적 타이핑이란 코드 구조 관점에서 타입이 서로 호환되는지의 여부를 판단하는 것입니다.

  • 즉 이는 객체 자신이 어떠한 타입인지를 중요하게 여기는 것 보다는 특정한 메서드나 속성의 존재를 통해 타입을 판단한다는 것입니다.
  • 이에 대해 공부하면서도 이부분의 이해가 문자로만 보기에는 이해가 어려웠는데 이에대한 쉬운 예시가 있다. 덕 타이핑이라고 검색하면 가장 많이 나오는 말이다.
    • If it walks like a duck and it quacks like a duck, then it must be a duck
    • 번역하면 오리처럼 걷고 소리를 내면 그건 분명 오리라고 결정한다.
  • TypeScript에서 객체는 정확히 단일 타입이 아닙니다. 만약 인터페이스를 만족하게되는 객체가 생성될때 객체와 인터페이스 사이의 관계를 선언하지 않더라도 인터페이스가 예상되는 곳에 할당 할 수 있습니다.
interface axis{
  x: number;
  y: number;

}

interface Named{
  name: string;

}

function printPoint(pointer: Pointlike){
  console.log("x = " + point.x + ",y = " + point.y);
}
function printName(x: Named) {
  console.log("Hello, " + x.name);
}

const object = {
  x: 0,
  y: 0,
  name: "Origin",
};

printPoint(object);
printName(object);

 

 

Type Inference ( 타입추론)


 

타입 추론은 타입스크립트가 코드를 해석해 나가는 동작을 의미한다.


  • 타입스크립트는 명시적인 타입에대한 표현이 없을 때 타입의 정보를 제공하기 위해 타입추론이 사용되는 위치가 여러가지입니다.
  • 해당 파트는 말로 설명하면 이해하기 어려운 부분이 많아 코드로 설명하겠습니다. 코드를 조금만 들여다보면 아마 아시는 내용일것 입니다.
let variable = 'data'; 
/***
/*
	해당 코드의 경우 variable은 자동으로 string으로 타입이 추론됩니다.
*/
  • 위 코드 같은경우 변수와 멤버를 초기화 하고 매개변수 기본값을 설정하고 함수 반환타입을 결정할때 발생하게 됩니다.
  • 조금 더 다양한 케이스를 보여드리게되면 크게 "최적 공통타입"과 "문맥상 타이핑"이 있습니다.

 

최적 공통 타입(Best common type)

let array = [ 1, 2, 3, null];
/***
/*
	위 와 같은 코드는 let array : (number | null)[] 와같은 형태로 타입을 추론하게된다.
*/
  • 표현식이 여러가지 인 경우 TS에서 타입을 추론할 때, 선언되어있는 타입을 통해서 타입을 추론합니다. 이를 "최적 공통 타입"이라고 Document에 표현되어있습니다.
  • number와 null 각각 후보 타입을 고려해서 다른 후보와 호환되는 타입을 결정한 후 추론됩니다.
  • 또한 우리는 일반적인 타입이 아닌 객체를 타입으로 선언할 때가 있는데 이 부분은 판단이 엄격합니다.
let zoo = [new Rion(), new Rabbit, new Elephant()];
/***
/*
	여기서 선언되는 타입은 let zoo : (Rion | Rabbit | Elephant)[]
*/
  • 위와같이 공통적인 구조를 공유하지만 하나의 타입으로서 모든 후보타입의 슈퍼가 되지 않는다면 엄격하게 판단하여서 배열요소의 타입으로 추론 할 수 없습니다. 방법은 아래와 같이 배열을 명시 해야하는 것입니다.
let zoo: Animal[] = [new Rion(), new Rabbit(), new Elephant()];

/***
/*
	여기서 선언되는 타입은 let zoo : Animal[]
*/
  • 위와 같이 공통 타입이 존재 하지 않는 경우 추론된 결과물은 처음 예시로들었던 zoo 코드와같이 타입이 선언되는데 그것을 유니언 배열 타입이라고 지칭합니다.[ex. (Rion | Rabbit | Elephant)[] ]

 

 

문맥상 타이핑( Contextual Typing )

  • 타입 스크립트에서 타입을 추론할 때에는 할당되는 표현식의 타입이 명확하지 않고 암시되더라고 발생합니다. 해당 부분의 이해는 어렵게 생각하면 너무 어렵게 다가오기때문에 그저 단순하고 멍청하게 봐야한다.

  • 아래의 코드를 단순하게 있는 그대로 바라보면 이해가 편할것 같다.

 

window.onmousedown = function(mouseEvent){
	console.log(mouseEvent.button); -- 오류없음
	console.log(mouseEvent.xxxxxx); -- 오류남
}
  • 위에서 타입스크립트의 검사는 window.onmousedown함수타입을 이용해서 표현식으로 나타낸 타입을 추론합니다. 이렇게 했을때 mouseEvent 객체의 매개변수 타입으로 button이라고 하는 프로퍼티는 있지만 xxxxx라는 말도안되는 이런 프로퍼티는 없다는것을 알수있다는 능력이 있습니다.

  • 위와같은 코드를 오류 없이 하고싶을때 사용하는 편법이 있습니다. 아래 코드를 먼저보시고 설명하겠습니다.
const handler = function(mouseEvent) {
  console.log(mouseEvent.xxxxxx); //<- 성공
}
  • 이렇게 함수가 문맥적으로 타입이 추론이되지 않는 위치에 있는상태이면 함수의 인수는 타입스크립트에서 암묵적으로 any( Java에서는 Object와 같음) 타입을 가지게 함으로서 오류를 보고 하지 않습니다.

  • 그 부분에대한 증명 코드를 아래에 작성한다면
window.onscroll = function(mouseEvent: any) {
    console.log(mouseEvent.xxxxxx);  //<- 이제 오류가 발생하지 않음
};
  • 위 코드에서 봤던 것 처럼 mouseEvent의 타입을 any로 생성한다면 xxxxx라는 말도안되는 프로퍼티를 넣더라도 오류가 발생하지 않습니다. 하지만 xxxxxx프로퍼티는 없기때문에 undefined라는 답을 가져올것입니다.

 

-- 추가예정

Union Types (유니온 타입)


유니온 타입이란 자바스크립트에서 OR 연산자 혹은 ( | | )와 같은 A이거나 B이다라는 의미의 타입입니다.
  • 어떤 언어든 사용하게되었을때 OR연산자를 사용하게되는데 기초적인 연산자이다.
  • 타입스크립트에서는 아래와 같은 형식으로 작성된다.
  •  
-- 사용예시
function testUnion(testVar : string | number){
	console.log('testVar 는 string타입 또는 number타입입니다');
}
  • TypeScript에서는 " | " 를 OR연산자로서 이용하고 이것을 유니온 타입이라고한다.
  • 유니온 타입을 사용하는 이유는 또한 아래코드를 보면 이해가 편하다.(해당코드는 타입스크립트 핸드북에서 들고옴)
// any를 사용하는 경우
function getAge(age: any) {
  age.toFixe(); // 에러 발생, age의 타입이 any로 추론되기 때문에 숫자 관련된 API를 작성할 때 코드가 자동 완성되지 않는다.
  return age;
}

// 유니온 타입을 사용하는 경우
function getAge(age: number | string) {
  if (typeof age === 'number') {
    age.toFixed(); // 정상 동작, age의 타입이 `number`로 추론되기 때문에 숫자 관련된 API를 쉽게 자동완성 할 수 있다.
    return age;
  }
  if (typeof age === 'string') {
    return age;
  }
  return new TypeError('age must be number or string');
}
  • 즉 이렇게 내가 원하느 타입의 데이터일때 들고와서 원하는데로 사용을하는 자바스크립트와 다른 타입스크립트만의 장점을 살릴 수 있다는것이다.

 

Type Guards ( 타입가드)


  • 타입스크립트는 언어의 이름자체에서도 언급될정도로 Type에 대한 특징이 다양합니다. 그 중 다른언어들과는 다르게 쓰이는 부분중에 하나는 다른 언어가 타입을 선언할때의 해당 객체는 선언한 타입을 지켜야하지만. 타입스크립트는 지키지 않을 수있는 방법이 있습니다.
  • 위에서도 설명한 유니온타입 입니다. 유니온타입을 통해 타입을 복수형태로 선언하게되면 매개변수는 어떠한 타입을 가져올지 개발자의 마음대로 결정 할 수 있는 유연한 형태를 가질 수 있습니다.
  • 이 부분은 그만큼 위험을 야기할 수 있습니다. 만약 아래와 같은 형태로 데이터가 오게된다면 해당하는 함수의 반환값이 무엇일지 예측하지 못할 수 있기때문입니다.
function exampleFunc(data: number | string ) : void{
  console.log(data + 1);   // 컴파일 오류가 잡힌다.
}

exampleFunc(1); // 2
exampleFunc('string'); // string1
  • 만약 위와 같은 함수를 만든다면 (위 함수는 지금은 컴파일오류를 잡아준다) string일때의 반환값과 number일때 반환값이 일정하지 않다. 그렇기때문에 타입스크립트에서는 타입을 추론하여 데이터를 원하는 방향으로 흘러 가게 도와주는 Type Guard가 존재 한다.

typeof

  • 별로 생소하지는 않은 연산자인데 JS를 사용하며 종종 사용해보았던 연산자인 typeof는 조건문을 이용해서 타입을 특정지어서 매개변수로 오는 데이터의 타입에따라 원하는 흐름으로 이동 시킬 수 있습니다.
function exampleFunc(data: number | string ) : void{
  if(typeof data === 'string'){
		console.log(data + '1');
		console.log(data.substr(1));// string의 올바른 프로퍼티
		console.log(data.sutr(1));// string의 올바르지않은 프로퍼티(컴파일오류)
	}
  if(typeof data === 'number'){
		console.log(data + 1);
	}
}

exampleFunc(1); //2
exampleFunc('string'); // string1
  • 이렇게 매개변수의 데이터에 따라서 컴파일 오류도 해결하면서 하나의 변수에 여러가지 타입을 가져오면 그것에 맞춰서 이동해 줄 수있습니다.

 

instanceof

  • instanceof의 경우 클래스 기반으로 생성한 인스턴스의 타입을 판단할 때 사용된다.
class Devlop{
	coding = true;
	common = 'job';
}

class Chef{
	cooking = true;
	common = 'job';
}

function worked(arg: Devlop | Chef){
	if(arg instanceof Devlop){
		console.log(arg.coding);  // true
		console.log(arg.cooking); // Error instanceof를 통해 클래스를 가져오는지를 이미 판단 한 이후라서
	}
	if(arg instanceof Devlop){
		console.log(arg.coding); // Error 위와같음
		console.log(arg.cooking); // true;
	}
	console.log(arg.common); // 'job'출력 instanceof가 없으면 둘다 만족할때만 가능하기때문에 잘나온다.
	console.log(arg.coding); // Error
	console.log(arg.cooking); // Error
}

in

  • in 오퍼레이터의 경우 프로퍼티의 존재 유무를 판단한다.
interface A {
	x: number;
}
nterface B {
  y: string;
}
function doStuff(q: A | B) {
  if ('x' in q) {
  // q: A
  }
  else {
  // q: B
  }
}

리터럴

  • 리터럴은 type으로 정의되어있는 타입에 대해, 타입스크립트 내부에서 지원하는 타입이 아닌 사용자 정의 타입에 대해 검사흘 수행 할때 사용가능하다.
type Foo = {
  kind: 'foo', // Literal type 
  foo: number
}
type Bar = {
  kind: 'bar', // Literal type 
  bar: number
}

function execute(arg: Foo | Bar) {
  if (arg.kind === 'foo') {
    console.log(arg.foo); // OK
    console.log(arg.bar); // Error!
  }
}
반응형