Today I Learned_230223
TypeScript
interface A {
talk: () => void;
}
const a : A = {
talk() {return 3;}
}
const b = a.talk(); //이 경우 b의 타입은 3이 아니라 void가 된다.
//void면 원칙적으로 리턴값이 없어야 하는 것이 맞다.
const c = a.talk() as number; //강제로 바꿔줘야 하는데 항상 바꿀 수는 없다. 이거로는 불충분
const d = a.talk() as unknown as number; //이렇게 해야지 바꿀 수 있다.
const e = <number><unknown>a.talk();
//앞에 꺽쇠보다는 as가 더 권장됨
//react의 jsx에 태그들이 쓰이는데, <>때문에 타입스크립트가 헷갈려서 잘 쓰기 힘들다.
//꺽쇠보다는 as를 써보자.
- unknown과 any의 차이점
- 우선 unknown 많이 사용한다.
- any는 타입 검사를 포기해버린다. 나 타입검사 안 할거라는 포기 선언
- unknown은 지금 당장은 타입을 정확하게 모를 때 사용. 지금은 잘 모르겠고 나중에 쓸 때 정의해서 쓰겠다. 포기와는 다르다.
- unknown은 타입을 정해줘야 한다.
- try catch의 error는 unknown이 되어야 한다.
- error는 어떤게 나올지 모름
- 원래는 any였다가 error로 바뀜
interface A {
talk:() => void;
}
const a : A = {
talk() {return 3;}
}
const b : any = a.talk();
b.method(); //.method()는 존재하지 않는데 여기서 오류라고 생각하지 않는다.
const b : unknown = a.talk();
b.method(); //unknown으로 하면 오류남
const b : unknown = a.talk();
(b as A).talk(); //unknown은 나중에 타입을 지정해서 사용
- 타입간 대입 가능 표 (파란색 표시랑 초록색은 다름)
- 초록색 : strict true. 그냥 X라고 생각하자.
- any는 void에 대입할 수 있다.
- null은 void에 대입할 수 없다.
- 넣었는데 any나 never가 나왔다면 의심해보자..
- 타입 가드(타입 좁히기)
- unknown일 때 빼고는 가급적이면 as 하지 말자.
- 남이 만든 타입이 틀렸을 때 as 쓰기..
- 타입스크립트는 if문 통해서 타입 구분할 수 있음
- 객체들간의 타입 검사를 할 때는 객체들간의 차이를 찾아야 한다.
- 리턴값에 is가 있는 것들은 커스텀 타입 가드 함수
- 커스텀 타입 가드는 if문 안에 사용해서 타입스크팁트에게 정확한 타입이 무엇인지 알려주는 것
- 타입 판별 함수는 직접 만들어야 한다.
- is가 아니면 타입 추론이 안 되는 경우도 있다.
function numOrStr(a: number | string) {
if (typeof a === 'string') {
a.split(',');
} else {
a.toFixed(1);
}
}
// 아래와 같이 했을 때 오류남(매개변수로 숫자만 줬을 때
function numOrStr(a: number | string) {
a.toFixed(1); //a가 string일 가능성도 있기때문에
}
//a임을 분명히 하려면 아래와 같이 하면 됨 - 근데 안된다
function numOrStr(a: number | string) {
(a as number).toFixed(1);
}
numOrStr('123') //이거 넣었을때 오류남 (자바스크립트 단에서 에러)
//if문 안에서 숫자인게 확실해진다.
function numOrStr(a: number | string) {
if (typeof a === 'number')
a.toFixed(1);
if(typeof a === 'boolean')
a.toString(); //파라미터로 boolean을 받고 있지 않기 때문에 a는 never가 됨
}
//else까지는 확인해준다.
//타입스크립트는 배열도 확인해 준다.
//또는 타입일 때는 타입을 잘 체크해 본다.
function numOrNumArr(a: number | number[]) {
if (Array.isArray(a)) { //number[]
a.slice(1);
} else {
a.toFixed(1); //number
}
}
//같은 속성인데 값이 다르거나('type')
//아예 속성 이름 자체가 다름
type B = { type: 'b', bbb: string }; //객체를 만들 때 type이라는 속성을 만드는 습관을 들이자(라벨/태그 느낌)
type C = { type: 'c', ccc: string };
type D = { type: 'd', ddd: string };
type A = B | C | D;
function typeCheck(a: A) {
if (a.type === 'b') { //객체 타입 안의 속성 가지고 구분할 수 있다.
a.bbb;
} else if (a.type === 'c') {
a.ccc;
} else {
a.ddd;
}
}
//이렇게도 가능
function typeCheck(a: A) {
if ('bbb' in a) { //a 객체 안에 bbb라는 속성이 있다
a.bbb;
} else if ('ccc' in a) {
a.ccc;
} else {
a.ddd;
}
}
//보통은 값으로 구분하는 방법을 선호함
interface Cat { meow: number }
interface Dog { bow: number }
function catOrDog(a: Cat | Dog): a is Dog { //return값에다가 is를 쓸 수 있음. 강아지이면 true 리턴
//타입 판별 직접 만들기
if ((a as Cat).meow) { return false }
return true;
}
//자바스크립트 문법 활용해서 타입 구분도 가능하지만
//타입을 구분해주는 커스텀 함수를 직접 만들 수 있다.
const cat: Cat | Dog = { meow: 3 }
if (catOrDog(cat)) { // 커스텀한 타입 가드
console.log(cat.meow);
}
if ('meow' in cat) { //자바스크립트 문법 활용한 타입 가능
console.log(cat.meow);
}
//타입스크립트 버전에 따라서 에러가 계속 달라진다.
//"target": "es2022","lib": ["es2020"],"module": "ES2022",
const isRejected = (input: PromiseSettledResult<unknown>): input is PromiseRejectedResult => input.status === 'rejected';
const isFulfilled = <T>(input: PromiseSettledResult<T>): input is PromiseFulfilledResult<T> => input.status === 'fulfilled';
const promises = await Promise.allSettled([Promise.resolve('a'), Promise.resolve('b')]);
const errors = promises.filter(isRejected);
//자바스크립트에서 에러 필터링 하려면 아래와 같이 하면 됨
const errors = promises.filter((promise) => promise.status === 'rejected')
- Promise는 실행하고 나면 Pending상태(비동기 실행 상태)가 되고 그 다음 Settled상태(완료지 성공인지 실실패인지 모름)가 된다.
- Settled에는 Fulfilled(성공), Rejected(실패)가 있다.
- 성공 여부와 상관 없이 Settled가 됨
- promises.then().catch()
- then : Fulfilled
- catch : rejected
- 둘 다 합친거 : settled
-
커스텀 타입 가드를 사용하면 정확한 타입을 사용할 수 있고 Promise에서 잘 필터링 할 수 있게 된다.
- class인 경우 instanceof 연산자도 가능!
- class는 instanceof 연산자를 통해 타입을 구분한다.
class A {
aaa() {}
}
class B {
bbb() {}
}
//클래스 이름 자체가 타입 자리에 올 수 있다.
//클래스 자체를 의미하는 것이 아니라 new A한 애들(인스턴스)을 의미
function aOrB(param: A | B) {
if(param instanceof A) {
param.aaa();
}
}
aOrB(A) //이거 안된다
빈 객체 타입{}과 Object
- 모양이 객체 같지만 모든 타입이라고 생각하자
- null과 undefined는 제외
- 겉보기에는 객체라 착각하기 쉬움
- 실제 객체는 소문자인 object에 해당
- 하지만 실제 객체를 만들 때는 object를 지양하고 interface, type, class를 사용하도록 하자.
- tsc 4.8부터는 아래 공식이 성립
- unknown = {} |null | undefined
- unknown은 모든 타입, null, undefined가 합쳐진 것이다
- unknown을 if 안에 넣으면 {}이 된다.
const x : {} = 'hello';
const y : Object = 'hi';
const xx : object = 'hi'; //객체 안에 string 넣는거 안 됨
const yy : object = {hello : 'world'};
const z : unknown = 'hi';
if(z) {
z; //z는 {}타입이 된다.
}