지금까지 타입스크립트를 사용하면서 타입스크립트도 결국에는 자바스크립트의 타입을 명시함으로서, 엄격한 검사 타입을 통해 에러를 최소화 하는 것이라고 느꼈다.
타입을 명시 할 때 interface
와 같이 객체 형태와 유사한 타입 지정 키워드가 있는데, 함수같은, 파라미터를 사용하여 조금더 유연하고, 동적인 타입 지정이 없을까? 라는 생각을 의문점을 가졌는데...
바로 이 <> 안에다가 .
함수 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 타입이다.
해결책은
함수에 <> 괄호를 열면 파라미터를 입력할 수 있는데,
여기에는 타입만 올수 있다.
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>
이런걸로 많이 함// 예시 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) 하는 것도 하나의 방법이다.
interface 문법에 쓰는 extends와는 다른 것
function 함수<MyType extends number>(x: MyType) {
return x - 1
}
let a = 함수<number>(100) //잘됩니다
문자로 파라미터를 넣으면 자릿수를 세어서 출력해주는 함수를 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 }) // 가능
이해가 안된다면
class Person <T> {
name;
constructor(a :T){
this.name = a;
}
}
let a = new Person<string>('어쩌구');
a.name //string 타입이 되었넹
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';