[ Typescript ] - Generic

최문길·2023년 12월 23일
1

Typescript

목록 보기
18/23

지금까지 타입스크립트를 사용하면서 타입스크립트도 결국에는 자바스크립트의 타입을 명시함으로서, 엄격한 검사 타입을 통해 에러를 최소화 하는 것이라고 느꼈다.


타입을 명시 할 때 interface 와 같이 객체 형태와 유사한 타입 지정 키워드가 있는데, 함수같은, 파라미터를 사용하여 조금더 유연하고, 동적인 타입 지정이 없을까? 라는 생각을 의문점을 가졌는데...





Generic을 사용하면 파라미터로 타입을 입력 할수 있다.

바로 이 <> 안에다가 .






함수 return 값의 타입이 애매하다면..

예를 들어 이렇게 array자료를 입력하면 array의 첫 자료를 출력해주는 함수가 있는데...


function 함수(x: unknown[]) {
  return x[0];
}

let a = 함수([4,2])
console.log(a)

이러면 콘솔창에 4가 출력은 된다.


근데 a의 타입을 확인해 보면 unknown 아입이다.
왜냐면 함수안에 넣은
[ 4, 2 ] 의 타입 지정을 안해줬기에
그냥 unknonw 타입으로 읽혀진다.

여기서 가장 중요한 것은
타입스크립트는 타입을 알아서 변경해주지 않는다.

함수의 파라미터의 타입을 unknown [ ]이라 정의 내리고 그 값에 타입을 명시하지 않은 배열을 넣으면 그대로 따라가는 것이다.

스마트하게 숫자가 return 되면 'number',
문자가 return 되면 'string' 타입이다 이런거 안해준다는 소리임.

그렇기에

function 함수(x: unknown[]) {
  return x[0];
}

let a = 함수([4,2])
console.log(a + 1) 

이러면 연산에러가 난다.
a의 타입은 unknown 타입으로 그래도 나오니까




그래서 함수에 불확실한 unknown, any, union 타입을 입력하면 나오는 값도 unknown, any, union 타입이다.

해결책은

    1. narrowing 잘하면 됨,
    1. 그냥 애초에 타입을 파라미터로 함수에 미리 입력하는 방법 Generic 이라고 한다.



Generic을 사용해보자



함수에 <> 괄호를 열면 파라미터를 입력할 수 있는데,

여기에는 타입만 올수 있다.


function 함수<MyType>(x:MyType[]) : MyType{
      return x[0]
}

let number = 함수<number>([4,2]);
let string = 함수<string>(['choi','park'])

이제 함수( ) 이렇게 쓰는 순간

MyType 이라는 변수에 number 라는게 들어간다고 보시면 됩니다.

그럼 이제
함수( x : number [ ] ) :number { ... } 이거랑 똑같이 동작한다.



function 함수<Type>(x:Type[]) : Type{
  return x[0]
  }
let a = 함수([4,2])
let b = 함수(['kim', 'park'])

근데 사실 꼭 <> 안써도 알아서 기본 타입을 유추해서 집어넣어준다.
위 코드도 결과는 똑같다 .

참고

  • 타입파라미터는 보통 <T> 이런걸로 많이 함
  • 일반 함수 파라미터처럼 2개 이상 넣는것도 가능


// 예시 1.
// 타입 파라미터 안에다 2개 타입 넣었다고 
//반드시 두개의 타입값이 있어야 하는것은 아니다.
function 함수<T,Y>(x:T[]) : T{
      return x[0]
}
let number = 함수<number,string>([4,2]); 


//  예시2.
// 2개의 파라미터 타입, 2개의 값
function 함수<T,Y>(x:T[], y: Y) : T{
      return x[0]
}
let number = 함수<number,string>([4,2],'choi');





주의 점


function func<MyType>(x: MyType) {
  return x - 1
}

let a = func<number>(100)

위와 같이 에러메세지 문구가 띄워지는데..
<MyType> 이라는 곳에 number 말고도 다른거 혹시 집어넣을 수 있으니까 저런 - 1 연산을 미리 방지해주는 것.

🙄 응??

할 수도 있는데... 처음에 내가 위 이모지 반응

func 라는 인자 값으로 숫자만 올수 있는것은 아니잖슴...

function func<MyType>(x: MyType) {
  return x - 1
}

이 식 '자체' 만 봤을 때 여러가지가 올 수 있는 것 아니겠슴?

해결책은 narrowing을 해도 되는데, MyType에 집어넣을 수 있는 타입을 미리 제한 (contaraints) 하는 것도 하나의 방법이다.





Generic 타입 제한하기 (constraints) - extends


extends 문법을 쓰면 넣을 수 있는 타입을 제한 할수가 있다 . 내가 받아들일 때는 제한이라는 의미보단, 최소한을 만족하는.. 이라는 의미로 받아들여진다.
그래서 My type extends number 라고 쓰면 타입 파라미터에 넣을 수 있는 타입을 제한가능하다.

interface 문법에 쓰는 extends와는 다른 것


interface의 extends는 복사인데 Generic에서 쓰는 extends는 if문으로 조건을 거는 문법이라고 보자.


function 함수<MyType extends number>(x: MyType) {
  return x - 1
}

let a = 함수<number>(100) //잘됩니다


이러면 에러없이 잘된다. return 타입지정을 안한 이유는 숫자 - 숫자를 했으니 알아서 number 타입된다.





Generic 응용


Generic- custom

문자로 파라미터를 넣으면 자릿수를 세어서 출력해주는 함수를 Generic으로 만들고 싶다 치면

function 함수<MyType>(x: MyType) {
  return x.length
}

let a = 함수<string>('hello')

이러면 에러가 나는데,

MyType에 string을 집어넣었지만 나중에 number 이런거 실수로 집어넣으면 어쩌겠나..

그래서 MyType을 extends 이걸로 제한하려고 했는데
이번엔 interface로 타입 만들어서 extends 해보자




interface lengthCheck {
  length : number
}
// 제네릭 T 는 반드시 { length: number } 프로퍼티 타입을 포함해 있어야 한다.
function 함수<MyType extends lengthCheck>(x: MyType) {
  return x.length
}

let a = 함수<string>('hello')  //가능
let a = 함수<number>(1234) //에러남
let a = 함수({ length: 10, value: 3 }) // 가능

이해가 안된다면

String과 Generic의 extends



Generic- class에서
class Person <T> {
  name;
  constructor(a :T){
    this.name = a;
  }
}
let a = new Person<string>('어쩌구');
a.name //string 타입이 되었넹 


Generic- interface & type alias

interface와 Generic

interface User<T>  {
      name: string,
      age: number,
      option: T
}


const user1 : User<{isMarreid:false,location:'Seoul'}> ={
      name: 'choi',
      age: 30,
      option : {isMarreid:false,location:'Seoul'}
}

const user2 : User<string> = {
      name: 'park',
      age: 31,
      option : 'good'
}


type alias와 Generic

type SorN<T> = T[] | T;

const number_arr: SorN<number> = [1, 2, 3, 4, 5];
const number_arr2: SorN<number> = 12345;

const string_arr: SorN<string> = ['1', '2', '3', '4', '5'];
const string_arr2: SorN<string> = '12345';

0개의 댓글