Today I Learned_230220
TypeScript
// A가 더 넓은 타입에 해당
type A = string | number; //string 또는 number
type B = string;
type C = string & number; // 이거는 불가능(never 타입으로 나온다)
- A가 B보다 더 넓은 범위 (A > B > C 순)
- any는 전체집합, never는 공집합으로 볼 수 있다.
- 좁은 타입에서 넓은 타입으로 대입 가능 (A에 B 넣을 수 있다)
- 넓은 타입에서 좁은 타입으로 대입 불가능 (B에 A 넣을 수 없다)
type D = {name : string};
type E = {age: number};
type F = {name: string, age:number};
//F는 오히려 좁은 타입이다. 속성이 적을수록 넓은 타입이다.
//객체는 상세(구체적일수록)할수록 더 좁은 타입이다.
type DE = D | E;
type DEF = D & E;
const de: DE = {name: 'heejung'};
const def: DEF ={name: 'heejung', age:29};
//const def: DEF ={name: 'heejung', age:29, married:false}; -> 이건 안 된다.
- 넓은 범위인 DEF에 구체적인 것을 넣었는데 왜 안되는거지? -> 객체 리터 잉여 속성 검사
- 객체 리터럴을 바로 넣으면 잉여 속성 검사(추가적인 검사) 진행
- 타입이 넓은지 좁은지에 이어 잉여 속성 검사를 추가적으로 진행한다.
- 함수의 경우 공변성/반공변성이 있다.
- 잉여 속성 검사 - 중간에 다른게 껴 있으면 검사하지 않는다. 바로 대입 시 참고할 것
type A = { hello: string };
const a: A = { hello: 'world', why: 'error' }; //이건 안 됨
const b = { hello: 'world', why: 'error' };
const c: A = b; //이건 됨
interface vs. type alias
//interface는 된다.
interface A {a:string}
interface A {b:string}
const obj1: A = {a: 'hello', b:'world'}
//type은 안 된다.
type B = {a:string}
type B = {b:string}
const obj2: B = {a:'hello', b:'world'}
//아래와 같은 상황에서 b는 자동으로 a를 void로 추론
function a() {
}
const b = a();
- void 타입은 return값을 사용하지 않겠다는 뜻(메서드나 매개변수에서는 리턴값 사용 가능, but 조심해야 함)
- function 선언할 때의 void와 메소드에서의 void는 다르다!
- undefined로 리턴하는 것은 가능
- null로 리턴하는 것은 불가능
- void는 리턴값을 아예 안 주거나 / return만 주거나 할 때 사용
- void는 총 3군데에서 사용
- 직접적인 리턴 값이 void인 경우 → 리턴값 있으면 안 됨
- 메소드의 리턴값을 사용하지 않겠다는 의미.
- 매개변수에 void함수가 들어간 경우 → 리턴값 있어도 됨
- 리턴값이 뭐든간에 상관하지 않겠다
- 메소드에 void함수가 들어간 경우 → 리턴값 있어도 됨
- 리턴값이 뭐든간에 상관하지 않겠다
- 직접적인 리턴 값이 void인 경우 → 리턴값 있으면 안 됨
function a(): void {
return 3; // void로 되어 있으면 여기서 에러가 난다.
}
//declare를 선언하면 구현부를 만들지 않아도 됨
//declare는 자바스크립트로 변환할 때 사라진다.
//undefiend -> 이 상태면 에러가 난다.
declare function forEach<T>(arr: T[], callback: (el: T) => undefined): void;
// 이렇게 void로 선언하면 오류가 나지 않는다.
// void의 리턴값이 무엇인지 상관하지 않겠다는 의미
// declare function forEach<T>(arr: T[], callback: (el: T) => void): void;
let target: number[] = [];
forEach([1, 2, 3], el => target.push(el)); //callback의 리턴값은 number인데 undefined로 정의하고 있기 때문에
//declare 없으면 아래와 같이 구현해야 함
function forEach(arr: number[], callback: (el: number) => undefined): void;
function forEach() {
}
interface A {
talk: () => void;
}
const a: A = {
//talk() => { return undefined}
talk() { return 3; } //그런데 이게 가능
}
- 타입만 선언하고 싶을 때 declare(구현은 다른 파일에 있어야 함)
- 다른 파일에 구현되어 있다는 것을 보장할 때 사용
- js에서는 다른 script 태그에 들어있다고 생각하기 때문에 js로 변환할 때는 문제가 되지 않는 것 처럼 보인다.
declare const a: string;
declare function a(x: number): number;
declare class A {}
// 추후 declare module, declare global, declare namespace도 배움
- void인지 아닌지에 따라 함수 표현에 제한이 된다.
declare function forEach(arr: number[], callback: (el: number) => undefined): void;
// 이 경우 둘 다 안 됨
forEach([1, 2, 3], el => {target.push(el)});
forEach([1, 2, 3], el => target.push(el));
declare function forEach(arr: number[], callback: (el: number) => number): void;
forEach([1, 2, 3], el => {target.push(el)}); //얘는 void 리턴이라 안 됨
forEach([1, 2, 3], el => target.push(el));
declare function forEach(arr: number[], callback: (el: number) => void): void;
//둘 다 돌아간다.
forEach([1, 2, 3], el => {target.push(el)});
forEach([1, 2, 3], el => target.push(el));
- void는 undefined와 다르다.
- 함수 리턴값의 void와 매개변수/콜백의 void, 메서드의 void는 서로 다르다.