9 분 소요

이 포스트는 현재 작성중입니다.
미완성된 내용이나 코드가 존재하므로 유의하시기 바랍니다.

타입스크립트 (TypeScript) : JavaScript을 확장하여 정적 타입을 지원하는 프로그래밍 언어

  • 정적 타입 (Static Type) : 변수, 매개변수, 함수 반환 값 등에 대한 타입을 명시적으로 지정 가능
  • 컴파일 언어 (Compile Language) : TypeScript 컴파일러는 TypeScript 코드를 JavaScript 코드로 변환
  • 타입 검사 (Type Checking) : 타입을 명시하지 않아도 코드에서 사용된 패턴과 값에 기반해 타입을 추론
npm init -y
npm install -g typescript ts-node
tsc -v
tsc --init
cat tsconfig.json
ts-node temp.ts

타입스크립트 컴파일러 (TSC) : TypeScript 코드를 JavaScript 코드로 변환하는 도구

  1. tsconfig.json 조회 : 프로그램 설정 및 파일 실행
  2. 사용 가능한 모든 파일들을 임포트 (import) (→ 파일들을 하나의 모듈 내에 있는 것처럼 관리)
  3. 추상 구문 트리 (AST; Abstract Syntax Tree)를 토큰화 (Tokenize) 및 파싱 (Parse)
  4. 바인더 (Binder)가 추상 구문 트리의 식별자를 Symbol로 변환
  5. 바인더와 추상 구문 트리로 타입 검사를 실행
  6. 프로그램에 설정한 옵션에 기반하여 추상 구문 트리를 *.js, *.d.ts의 형태로 변환

타입 시스템 (Type system) : 프로그램에서 가질 수 있는 타입을 이해하는 방법에 대한 규칙 집합

  1. 코드를 읽고 존재하는 모든 타입과 값을 이해한다.
  2. 각 값이 초기 선언에서 가질 수 있는 타입을 확인하고, 각 값이 추후 코드에서 어떻게 사용될 수 있는지 확인한다.
  3. 모든 방법을 확인했을 때, 값의 사용법이 타입과 일치하지 않으면 사용자에게 타입 오류로 표시한다.

타입 오류 vs 구문 오류?

  • 타입 오류 (Type Error) : 타입 검사기에 따라 일치하지 않는 것이 감지된 경우
    • 타입스크립트의 타입 검사기가 프로그램의 타입에서 오류를 감지했을 때 발생
  • 구문 오류 (Syntax Error) : 타입스크립트가 자바스크립트로 변환되는 것을 차단한 경우
    • 타입스크립트가 코드로 이해할 수 없는 잘못된 구문을 감지할 때 발생

할당 가능성 (Assignability) : 함수 호출이나 변수에 값을 제공할 수 있는지 여부를 확인 가능

  • TypeScript에선 변수나 속성에 값을 할당할 때 해당 값의 타입이 변수나 속성의 타입과 호환 가능해야함
let firstName = "Noah";
firstName = "Tom";

타입 어노테이션 (Type Annotation) : 초기값을 할당하지 않고도 타입을 명시적으로 지정 가능

메모리 사이즈가 변하지 않는 변수는 타입 어노테이션을 추가하지 않는 것이 좋으나, 코드를 명확하게 문서화하거나 실수로 변수 타입이 변경되지 않도록 타입스크립트를 보호하기 위해 변수에 명시적으로 타입 어노테이션을 포함하는 것이 경우에 따라서는 유용할 수 있다.

let variable: Type; // 변수의 이름 : 값의 타입

타입 형태 (Type Shape) : 타입을 정의할 때 객체의 형태를 명시적으로 지정 가능

  • 인터페이스 (Interface) : 객체의 구조를 정의
interface Person {
    name: string;
    age: number;
}

let person: Person = {
    name: "John",
    age: 25
};
  • 타입 (Type) : 객체의 타입을 정의
type Point = {
    x: number;
    y: number;
};

let point: Point = {
    x: 10,
    y: 20
};

타입 별칭 (Alias) : 자주 사용할 타입에 이름을 붙이는 것

타입 별칭은 타입 어노테이션과 마찬가지로 자바스크립트로 컴파일되지 않으므로, 런타임 코드에서는 참조할 수 없다.

type Age = number;
type Name = string;

type Person = {
    name: Name;
    age: Age;
};


let person: Person = { name: "John", age: 25 };

리터럴 타입 (Literal Type) : 원시 타입보다 더 구체적인 원시 값 자체를 타입으로 활용하는 것

  • 특정 값들을 타입으로 정의하고, 해당 값들만이 유효한 값으로 간주
    • 리터럴 할당 가능성 : 서로 다른 리터럴 타입은 서로 할당될 수 없음
let isString = '문자열';
let isLiteral: 'LITERAL';

isLiteral = isString;
// Error type 'string' is not assignable to type '"LITERAL"'.

유니언 타입 (Union Type) : 값에 허용되는 타입을 두 개 이상의 가능한 타입으로 확장하는 것

  • 둘 이상의 타입으로 확장된 타입에서, 일부 속성들의 조합이 하나의 타입에 할당 가능하면 유니온 타입으로 사용 가능
    • 유니언으로 선언한 모든 타입에 존재하는 속성에만 접근 가능!
type Person = {
   name: string;
   age: number;
   phone: number | string; // number 또는 string 타입을 가질 수 있음
   addr?: string; // 선택적으로 사용할 수 있음
};

내로잉 (narrowing) : 값이 더 구체적인 타입임을 코드에서 유추하는 것

  • 타입스크립트에서 변수의 타입을 조건문을 통해 줄여나가는 것
    • 값 할당 ex) x = 1
    • typeof 검사 ex) typeof(value)
    • 조건 검사 : ex) if x === 'stringValue'
    • in, instanceof, Array.isArray를 활용

내로잉을 수행하는 함수 또는 구문을 타입 가드 (Type guard)라 한다.

type Member = {
  name: string;
  address: string;
}

function inFunc (user: Member) {
  if ('name' in user) // cf. user.hasOwnProperty('name')로는 불가능
    console.log('user.name); 
}

strictNullChecks : 엄격한 null 검사 활성화 옵션

  • null 혹은 undefined 값을 참조 및 할당 했을 때, 타입 에러를 발생시킬지 여부
// tsconfig.json 
//  {
//    "compilerOptions": {
//      "strictNullChecks": true
//    }
//  }

let un: string | undefined;
un.toLowerCase(); // Type 'undefined' is not assigned to type 'string'
un?.toLowerCase();

객체 타입 (Object Type) : 객체의 형태를 정의하고, 객체의 할당 가능성을 확인

  • 타입스크립트의 타입 시스템은 타입을 구조화하여 정의하고 있음 → Structured Type Definition
    • 타입 체크 시스템 (Type Check System) 을 통해 구조화된 형식을 검사
let x_user: {id: number, name: string};
x_user = {id: 1, name: 'xx'}; // OK
x_user = {id: 1}; // Error (Property 'name' missing in type)
x_user = {id: 1, name: 'xx', age: 30}; // Error ({id, name, age} is not assignable to type {id,name} )

// 타입 별칭(type alias)
type T_User = {
  id: number;
  name: string;
};

let hong: T_User;
hong = {id: 1, name: 'Hong'}; // OK
hong = {id: 1}; // Error (name property missing)
hong = {id: 1, name: 'Hong', addr: 'Pusan'}; // Error(not assignable) 
hong = {id: 1, name: 'Hong', addr: 'Pusan'} as T_User;

타입스트립트는 CoVariance | ContraVariance를 원칙으로 하는 언어?

  • 공변성 (CoVariance) : 원래 지정된 것보다 더 파생된 형식을 사용 가능한 것
    → 타입 간의 계층 구조가 유지되면 변환을 허용 ex) 배열
let baseArray: Animal[] = [];
let derivedArray: Dog[] = [];

// Covariant: 하위 타입인 Dog[]가 상위 타입인 Animal[]로 할당 가능
baseArray = derivedArray;
  • 반변성 (ContraVariance) : 원래 지정된 것보다 덜 파생적인 형식을 사용 가능한 것
    → 하위 타입이 하위 타입으로 할당될 수 있음 ex) 함수의 매개변수
type AnimalHandler = (animal: Animal) => void;
type DogHandler = (dog: Dog) => void;

// Contravariant: 상위 타입인 AnimalHandler가 하위 타입인 DogHandler로 할당 가능
let animalHandler: AnimalHandler = (animal: Animal) => { /*...*/ };
let dogHandler: DogHandler = animalHandler;

타입스크립트는 CoVariance를 원칙이나, 함수의 매개변수처럼 ContraVariance가 적용되는 경우 또한 존재한다.

신선도 (Freshness) : 구조적으로 타입 호환성이 있는 객체 리터럴의 타입 검사

  • 신선도로 인한 오류는 ‘객체 리터럴을 사용한 경우에만’ 발생한다.
function logName(something: { name: string }) {
    console.log(something.name);
}

var person = { name: 'John', job: 'cowboy' };
var animal = { name: 'cow', description: 'element who hate John' };
var event = { note: `random` };

logName(person);
logName(animal);
logName(event); // 오류: 속성 `name` 누락 (신선도로 인한 오류 O)

function logName(something: { name: string }) {
  console.log(something.name);
}

logName({ name: 'John' });
logName({ name: 'John', job: 'cowboy' }); // 오류: 객체 리터럴은 정의된 속성만 지정해야 함. (신선도로 인한 오류 X)

신선도를 끄는 방법?

  1. 변수에 할당한다.
  2. 강제로 타입 캐스팅 (type casting)을 한다.
  3. union 타입으로 제외시킨다.
  4. suppressExcessPropertyError를 활성화한다.

함수 타입 (Function Type) : 함수가 가져야 하는 매개변수와 반환 타입을 명시

  • 변수에 할당되거나 매개변수로 전달되는 함수의 타입을 정의
    • 필수 매개변수 : 함수에 선언된 모든 매개변수를 필수라고 가정 → 타입 안정성이 강화
    • 선택 매개변수 : 타입 애너테이션의 : 앞에 ?를 추가하여, 매개변수가 선택적이란 것을 표시
    • 기본 매개변수 : 기본적으로 값을 제공되므로, 함수 내부에 암묵적으로 | undefined union type이 추가
      → 타입스크립트는 함수의 매개변수에 대해 인수를 누락하거나 undefined 인수를 사용한 호출을 허용
    • 나머지 매개변수 : 함수 선언의 마지막 매개 변수에 위치한 ... 스프레드 연산자로 표현
      → 해당 매개변수에서 시작해 함수에 전달된 나머지 인수가 모두 단일 배열에 저장
      (인수 배열을 나타내기 위해 끝에 [] 구문이 추가)
function add(a: number, b: number) {
   return a + b;
}

type ANY = any;
function addY (a: ANY, b: ANY) {
  return a + b;
}

function addZ (a: ANY, b?: ANY) {
  return a + (b ?? 0);
}

addX(1, 0); addY(1, 0); addZ(1, 0);
addZ(1); // addX(1); addY(1);

const getSum = (...rest:number[]) =>{
 let sum = 0;
 rest.forEach((el) => sum += el);
 return console.log(sum);
}

?으로 표시된 선택 매개변수가 아닌 필수 매개변수는 값이 명시적으로 undefined이라도 항상 제공되어야 한다.

const introduce2 = (name:string, 

height : number|undefined ) => {
   console.log(`이름 : ${name}`);

if(typeof height === 'number'){
   	console.log(`키 : ${height + 10}`)   
 	}
}

introduce2("김군"); // Error : Expected 2 arguments, but got 1.
introduce2("김군", undefined);
introduce2("김군", 170);
  • 함수의 반환 타입 : 함수가 반환할 수 있는 가능한 모든 값을 이해하면 함수가 반환하는 타입을 알 수 있음
    • 그러나, 함수에서 반환 타입을 명시적으로 선언하는 방식이 유용할 때가 종종 있음
function singSongRecursive(songs : string[], count = 0) : number {
    return songs.length ? singSongRecursive(songs.slice(1), count + 1) : count;
}

const singSongsRecursive = (songs : string[], count=0) : number =>
 songs.length ? singSongsRecursive(songs.slice(1), 
count + 1) : count;
  • 함수 매개변수 타입 : 함수를 가지기 위한 매개변수 또는 변수의 타입을 선언하는 방법
    • 함수 타입 구문은 화살표 함수와 유사하지만 함수 본문 대신 타입이 존재
let nothingGivesString : () => string;

let inputAndOutput : (songs: string[], count?: number) => number;
  • 함수 타입 괄호 : 함수 타입은 다른 타입이 사용되는 모든 곳에 배치할 수 있음 (유니온 타입 포함)
    • 유니언 타입의 애너테이션에서 함수 반환 위치를 나타내거나 타입을 감싸는 부분을 표시할 때 괄호 사용
// 타입은 string | undefined 유니언을 반환하는 함수
let returnsStringOrUndefined : () => string | undefined;

// 타입은 undefined나 string 을 반환하는 함수
let maybeReturnsString : (() => string) | undefined;
  • 매개변수 타입 추론 : 선언된 타입의 위치에 제공된 함수의 매개변수 타입을 유추할 수 있음
    • 또한, 함수를 매개변수로 갖는 함수에 인수로 전달된 함수는 해당 매개변수 타입도 유추할 수 있음
let singer : (song : string) => string;

singer = function(song){
    // song : string의 타입
    return `Singing : ${song.toUpperCase()}!`; // OK
}

const songs = ["One More Time", "I AM", "Cry"];

// song : string
// index : number

songs.forEach((song, index) => {
    console.log(`${song} is at index ${index}`);
});

함수 오버로딩 (Function Overloading) : 동일한 이름에 매개 변수만 다른 함수를 여러 개 만드는 것

  • 매개변수의 형태가 다양한 여러 케이스에 대응하는 같은 이름의 함수를 만드는 것
// 서로 다른 버전의 함수들 -> `오버로드 시그니처`
function func(a : number): void;
function func(a : number, b : number, c : number) : void;

// 실제 구현부 -> `구현 시그니처`
function func(a:number, b?: number, c?:number) {
  if(typeof b === 'number' && typeof c === 'number') {
    console.log(a + b + c);
  } else {
    console.log(a + b + c);
  }
}

func(1); func(1, 2, 3);

인터페이스 (Interface) : 타입을 정의할 때 객체의 구조를 명시적으로 지정 가능

호출 시그니처 (Call Signature) : 함수 호출 방법에 대한 타입 시스템을 설명

  • 매개변수의 목록 (with type)과 반환 타입을 포함
type FunctionAlias = (input: string) => number;

interface CallSignature {
    (input: string): number;
}

const typedFunctionAlias: FunctionAlias = (input) => input.length;

const typedCallSignature: CallSignature = (input) => input.length;

호출 시그니처는 사용자 정의 속성을 추가로 갖는 함수를 설명하는 데에도 사용할 수 있다.

인덱스 시그니처 (Index Signature) : 임의의 키를 받고, 해당 키에 대한 값의 타입을 지정

인덱스 시그니처를 사용하면, 프로퍼티의 존재 유무를 알 수 없다!

중첩 시그니처 (Nested Interface) : 속성의 타입이 다른 인터페이스나 객체 타입을 가질 수 있음

클래스 속성 (Class Attribute) : 클래스의 속성을 읽거나 쓰려면 명시적으로 선언해야 함

class FieldTrip{
    destination  : string; // 불필요

    constructor(destination : string){
        this.destination = destination;
        console.log(`We're going to ${destination}!`);

    this.nonexistent = destination;
    //Error: Property 'nonexistent' does not exist on type 'FieldTrip'
    }
}

클래스 메소드 (Class Method)

class Greeted {
    constructor(message : string){
        console.log(`As I always say : ${message}!`);
    }
}
//Greeted 클래스의 생성자는 message:string으로 매개변수가 제공되어야 함
new Greeted("Practice makes perfect.");

태그:

업데이트: