타입스크립트 타입

fe_sw·2022년 8월 7일
0

Typescript

목록 보기
2/2
post-thumbnail

타입 지정

타입스크립트는 일반 변수, 매개 변수, 객체 속성 등에: TYPE 같은 형태로 타입을 지정할 수 있다.

let a: string = 'text'; // 문자열
let b: number = 0; // 숫자형
let c: boolean = true; // 논리형
let d: any = true; // 어떤 타입이 올지 모를 때
let e: string | number = '0'; // 문자열이나 숫자가 올 때

타입 에러

만약 아래와 같이 타입을 선언하고 다른 타입의 값을 대입하거나 값이 선언된 후에 다른 타입의 값이 대입되면 에러를 발생한다.

// 문자열로 선언하고 숫자를 대입하면 에러 발생
let a: string = 0; // error

// 문자열 값이 들어가고 이후에 숫자가 대입하면 에러 발생
let b: string = 'text';
b = 1; // error Type '1' is not assignable to type 'string'.  TS2322

타입 선언

타입스크립트는 ES5, ES6의 상위 확장인 언어이므로 자바스크립트의 타입을 그대로 사용하며, 이외에도 타입스크립트의 고유의 타입이 추가로 제공된다.

TypeJSTSDescription
booleantrue와 false
null값이 없다는 것을 명시
undefined값을 할당하지 않은 변수의 초기값
number숫자(정수와 실수, Infinity, NaN)
string문자열
symbol고유하고 수정 불가능한 데이터 타입이며 주로 객체 프로퍼티들의 식별자로 사용(ES6에서 추가)
object객체형(참조형)
array배열
tuple고정된 요소수 만큼의 타입을 미리 선언후 배열을 표현
enum열거형. 숫자값 집합에 이름을 지정한 것
any타입 추론(type inference)할 수 없거나 타입 체크가 필요없는 변수에 사용
void일반적으로 함수에서 반환값이 없을 경우 사용
never결코 발생하지 않는 값

논리형(Boolean)

let bool01: boolean = true;
let bool02: boolean = false;

숫자형(Number)

let num01: number = 5;
let num02: number = 3.14;
let num03: number = NaN;

문자열(String)

ES6의 템플릿 문자열도 지원한다.

let str01: string = 'text';
let str02: string = `my name is ${val}`;
배열(Array)
배열은 아래와 같이 두가지 방법으로 타입을 선언할 수 있다.

// 문자열만 가지는 배열
let arr01: string[] = ['a', 'b', 'c'];
let arr02: Array<string> = ['a', 'b', 'c'];

// 숫자형만 가지는 배열
let arr03: number[] = [1, 2, 3];
let arr04: Array<number> = [1, 2, 3];

// Union 타입(다중 타입)의 문자열과 숫자를 동시에 가지는 배열
let arr05: (string | number)[] = [1, 'a', 2, 'b', 'c', 3];
let arr06: Array<string | number> = [1, 'a', 2, 'b', 'c', 3];

// 배열이 가지는 값의 타입을 추측할 수 없을 때 any를 사용할 수 있다.
let arr07: (any)[] = [1, 'a', 2, 'b', 'c', 3];
let arr08: Array<any> = [1, 'a', 2, 'b', 'c', 3];
배열이 가지는 항목의 값을 단언할 수 없다면 any를 사용할 수 있다.

let someArr: any[] = [0, 1, {}, [], "str", false];
함수(Function)
함수는 파라미터에 각각 타입을 선언해 주며, 파라미터 우측에는 해당 함수의 리턴값 타입도 선언해 주면 된다.

function sum(a: number, b: number): number {
  return a + b;
}
console.log(sum(2, 3)); // 5
리턴값을 숫자형으로 선언하였는데 다른 값이 리턴된다면 역시 에러가 난다.

function sum(a: number, b: number): number {
  return null; // error
}

객체(Object)

기본적으로 typeof 연산자가 object로 반환하는 모든 타입을 나타낸다. 여러 타입의 상위 타입이기 때문에 그다지 유용하지 않다.

let obj: object = {};
let arr: object = [];
let func: object = function() {};
let date: object = new Date();

보다 정확하게 타입 지정을 하기 위해 아래와 같이 객체 속성들에 대한 타입을 개별적으로 지정할 수 있다.

let user: { name: string, age: number } = {
  name: 'a',
  age: 20
};
console.log(user); // {name: "a", age: 20}

object extend

type PositionX = { x: number };
type PositionY = { y: number };
type XandY = PositionX & PositionY

let 좌표 :XandY = { x : 1, y : 2 }

인터페이스(Interface)나 커스텀 타입을 사용할 수도 있다.

interface IUser {
  name: string;
  age: number;
  isVaild: boolean;
}

let userArr: IUser[] = [
  {
    name: "Neo",
    age: 10,
    isVaild: true,
  },
  {
    name: "Lewis",
    age: 64,
    isVaild: false,
  },
  {
    name: "Evan",
    age: 123,
    isVaild: true,
  },
];

읽기 전용 배열을 생성할 수 있다. readonly ReadonlyArray 타입 사용

let arrA: readonly number[] = [1, 2, 3, 4];
let arrB: ReadonlyArray<number> = [2, 4, 6, 8];
arrA[0] = 123;

튜플(Tuple)

배열과 유사하다. 차이점은 정해진 타입의 요소 개수 만큼의 타입을 미리 선언후 배열을 표현한다.

let tuple: [string, number];
tuple = ['a', 0];
console.log(tuple); // ["a", 0]

정해진 요소의 순서 및 개수가 다르면 오류를 출력한다.

let tuple: [string, number];
tuple = [0, 'a']; // error
tuple = ['a', 0, 1]; // error

아래와 같이 데이터를 개별 변수로 지정하지 않고, 단일 튜플 타입으로 지정해 사용할 수 있다.

// 기존 변수
let name: string = 'a';
let age: number = 20;

// 튜플
let user: [string, number] = ['a', 20];
console.log(user); // ["a", 20]

값으로 타입을 대신할 수도 있다. 처음 선언할 때의 값과 다른 값이 할당 되면 에러가 출력된다.

let user: ['a', number];
user = ['a', 20]; // ["a", 20]
user = ['a', 30]; // ["a", 30]
user = ['b', 30]; // error

튜플은 정해진 타입과 고정된 길이의 배열이지만, 값을 할당할 때만 해당된다. push나 splice같은 메소드를 통해 값을 넣는건 막을 수 없다.

let user: [string, number];
user = ['a', 20]
console.log(user); // ["a", 20]
user.push(30);
console.log(user); // ["a", 20, 30]

열거형(Enum)

숫자 혹은 문자열 값 집합에 이름을 부여할 수 있는 타입으로, 값의 종류가 일정한 범위로 정해져 있는 경우 유용하다. 기본적으로 0부터 시작하며, 값은 1씩 증가한다.

enum obj {
  a,
  b,
  c,
  d,
  e
}
console.log(obj);
// 0: "a"
// 1: "b"
// 2: "c"
// 3: "d"
// 4: "e"
// a: 0
// b: 1
// c: 2
// d: 3
// e: 4

수동으로 값을 변경할 수 있으며, 변경한 부분부터 다시 1씩 증가한다.

enum obj {
  a,
  b = 10,
  c,
  d,
  e
}
console.log(obj.b); // 10
console.log(obj.c); // 11

모든 타입(Any)

Any는 모든 타입을 의미하며, 기존의 자바스크립트 변수와 마찬가지로 어떠한 타입의 값도 할당할 수 있다. 불가피하게 타입을 선언할 수 없는 경우, 유용할 수 있다.

let any:any = 'String';
any = 0;
console.log(any); // 0
any = true;
console.log(any); // true

빈 타입(Void)

빈 타입인 Void는 리턴값이 없는 함수에서 사용된다. 리턴값의 타입을 명시하는 곳에 작성하며, 리턴값이 없는 함수는 undefined를 반환한다.

function hello(): void {
  console.log("hello");
}
console.log(hello()); // undefined

알 수 없는 타입: Unknown

any와 같이 최상위 타입인 unknown은 알 수 없는 타입을 의미한다.
어떤 타입의 값도 할당할 수 있지만, unknown을 다른 타입에 할당할 수 없다.

let u: unknown = 123;
let test1: number = u; //알 수 없는 타입(unknown)은 모든 타입(any)을 제외한 다른 타입에 할당할 수 없습니다.
let test2: number = u as number; //타입을 단언하면 할당할 수 있다.
let test3: any = u; // any와 unknown은 서로 할당할 수 있다.

Null과 Undefined

기본적으로 null, undefined는 모든 타입의 하위 타입으로, 각 타입에 할당할 수 있다.
서로의 타입에도 할당 가능하다.

let num: number = undefined;
let str: string = null;
let obj: { a: 1, b: false } = undefined;
let arr: any[] = null;
let und: undefined = null;
let nul: null = undefined;
let voi: void = null;

컴파일 옵션에서 "strictNullChecks": true를 통해 엄격하게 Null과 undefined 서로의 타입까지 할당 할 수 없게 한다. 단, void에는 undefined할당 가능

let vod: void = undefined;

never

Never 타입은 절대 발생할 수 없는 타입을 나타낸다.

function errorMsg() {
  throw new Error("오류 발생!");
}
console.log(errorMsg()); // Uncaught Error: 오류 발생!

errorMsgd 함수는 오류를 발생시키기 때문에 null, undefined를 포함한 어떠한 값도 반환하지 않는다. 이럴 경우 never 타입을 사용하면 된다.

유니언(Union)

2개 이상의 타입을 허용하는 경우, 이를 유니언이라 한다.
| (vertical bar)를 이용해 타입을 구분하며, ()은 선택 사항.

let union: (string | number);
union = 'Hello World';
union = 777;
union = false; // Error - TS2322: Type 'false' is not assignable to type 'string | number'.
// string, number 타입만 허용하기 때문에 에러.

인터섹션(Intersection)

&(ampersand)를 사용해 2개 이상의 타입을 조합하는 경우, 이를 인터섹션이라고 한다
인터섹션은 새로운 타입을 생성하지 않고 기존의 타입들을 조합할 수 있기 때문에 유용하지만 자주 사용되지는 않는다.

interface User {
  name: string,
  age: number
}

interface Validation {
  isValid: boolean
}

const testCase: User = {
  name: 'juyoung',
  age: 27,
  isValid: true // Error -  TS2322: Type '{ name: string; age: number; isValid: boolean; }' is not assignable to type 'User'.
};

const testCase2: user & Validation = {
  name: 'jisu',
  age: 30,
  isValid: true
}

interface

자바에서 인터페이스는 클래스를 구현하기 전에 필요한 메서드를 정의하는 용도로 쓰이지만, 타입스크립트에서는 좀 더 다양한 것들을 정의하는 데 사용된다.

인터페이스로 타입을 정의할 때는 interface 키워드를 사용한다.

interface Person {
    name: string;
    age: number
}

const p1: Person = { name: 'mike', age: 23};
const p2: Person = { name: 'mike', age: 'ten'};

Person 인터페이스를 정의하고 객체 내부에 존재하는 각 속성의 타입을 정의한다. p2는 age가 number 여야 되는데 string을 넣어서 타입 에러가 났다.

interface Person {
    name: string;
    age?: number;
}
const p1: Person = { name: 'mike' };

? 기호를 붙이면 선택 속성으로 할 수 있다.

interface Person {
    readonly name: string;
    age?: number;
}

const p1: Person = {
    name: 'mike',
}
p1.name = 'jone'; // 컴파일 에러

정의 되지 않은 속성값을 갖고 있어도 할당이 가능하다. 단, 리터럴로 값을 초기화하는 경우에는 인터페이스에 정의되지 않은 속성값이 있으면 타입에러가 발생한다.

리드온리는 속성변경불가

interface Person {
    readonly name: string;
    age?: number;
}

const p1: Person = { // Person에 birthday가 없으므로 에러가 난다.
    name: 'mike',
    birthday: '1997-01-01', 
}; // 타입에러
const p2 = {
    name: 'mike',
     birthday: '1997-01-01', 
};
const p3: Person = p2; // 에러가 안 난다. p3타입이 p2타입을 포함하는 더 큰 타입이기 때문이다.

interface와 type alias의 차이점

재정의 가능

interface Name= string

interface Name= number 

extends 가능


interface Student {
  name :string,
}
interface Teacher {
  name :string,
  age :number,
}

//중복되는거  extends 해줌 (class랑 똑같음)

interface Student {
  name :string,
}
interface Teacher extends Student {
  age :number
}


//type alias경우 이런식으로

type Animal = { 
  name :string 
} 
type Cat = Animal & { legs: number }

타입이름 중복선언시

interface Animal { 
  name :string 
} 
interface Animal { 
  legs :number 
}
interface의 경우 타입이름 중복선언을 허용해주며 
중복시 extends 한 것이랑 동일하게 동작합니다. 

이러면 Animal 타입은 name, legs 속성을 가질 수 있습니다. 


type Animal = { 
  name :string 
} 
type Animal = { 
  legs :number 
}
type의 경우 중복선언을 허용하지 않습니다. 에러남

object 속성 중복시

interface Animal { 
  name :string 
} 
interface Dog extends Animal { 
  name :number 
}
Animal을 복사해서 Dog interface를 만들어봤습니다.

근데 name 속성이 중복되네요? 그럼 에러납니다



interface Animal { 
  name :string 
} 
interface Dog { 
  name :number
} 

let 변수 :Dog & Animal = { name : '멍멍' }

근데 name 속성이 중복되네요? 그럼 에러납니다
interface 말고도 type 키워드도 똑같은 현상이 일어납니다.
(주의) 근데 name : string , name : number 라서 에러가 나는 것이지
둘다 name : string 타입이면 에러가 나지 않습니다. 하나로 합쳐줌

타입 별칭 (Type Aliases)

타입 별칭은 타입의 새로운 이름을 생성한다. 프로그래머의 의도를 보다 명확하게 나타낼 수 있다.
custom type이라고 부르기도한다.

기본 타입을 포함한 모드타입이 올 수 있다.

type Name = string;
type Age = number;
type CombineNumberString = string | number;
type User = {
    name: string;
    heihgt: number;
}

함수 type aliases

type NumOut = (x : number, y : number ) => number ;

let ABC : NumOut = function(x,y){
  return x + y
methods type aliases

type Member = {
  name : string,
  age : number,
  plusOne : ( x :number ) => number,
  changeName : () => void
}

let 회원정보:Member  = {
  name : 'kim',
  age : 30,
  plusOne (x){
    return x + 1
  },
  changeName : () => {
    console.log('안녕')
  }
}

index signature

어떤 속성이 들어갈지 아직 모른다면
그냥 전부 싸잡아서 타입지정도 가능합니다.

  interface IPerson {
    [key: string]: string;
  }

  const person: IPerson = {};

  person['name'] = 'Yeonju';  //ok!!
  const foo: string = 'foo';
  person[foo] = 'hello';      //ok!!

Literal Types

TypeScript에는 문자열과 숫자, boolean 세 가지 리터럴 타입이 있는데 이를 사용하면 문자열이나 숫자에 정확한 값을 지정할 수 있습니다.

Literal Narrowing

const로 변수를 선언하게 되면 TypeScript에게 이 객체는 절대 변경되지 않음을 알립니다.

const helloWorld = "Hello World";

let hiWorld = "Hi World";

위 예시에서 helloWorld는 문자열이 아닌 "Hello World"로 타입을 정하지만 hiWorld는 변경될 수 있으므로 문자열로 선언합니다.

이렇게 잠재적 케이스의 수를 줄이는 것을 Narrowing 이라고 부릅니다.

String Literal Types

문자열 리터럴 타입은 Union types, type guards, type aliases과 잘 결합됩니다. 이런 기능을 통해 문자열을 enum처럼 사용할 수 있습니다.

type Easing = "ease-in" | "ease-out" | "ease-in-out";

class UIElement {
  animate(dx: number, dy: number, easing: Easing) {
    if (easing === "ease-in") {
      // ...
    } else if (easing === "ease-out") {
    } else if (easing === "ease-in-out") {
    } else {
      // 하지만 누군가가 타입을 무시하게 된다면
      // 이곳에 도달하게 될 수 있습니다.
    }
  }
}

let button = new UIElement();
button.animate(0, 0, "ease-in");
button.animate(0, 0, "uneasy");

Numeric Literal Types

Typescript에는 숫자형 리터럴 타입도 있습니다.

function rollDice(): 1 | 2 | 3 | 4 | 5 | 6 {
  return (Math.floor(Math.random() * 6) + 1) as 1 | 2 | 3 | 4 | 5 | 6;
}

Boolean Literal Types

interface ValidationSuccess {
  isValid: true;
  reason: null;
};

interface ValidationFailure {
  isValid: false;
  reason: string;
};

type ValidationResult =
  | ValidationSuccess
  | ValidationFailure;

0개의 댓글