//함수 오버로딩 사용시
function toObj(a:string, b:string): {a:string, b:string}
function toObj(a:number, b:number): {a:number, b:number}
function toObj(a:string | number, b:string | number): {a:string | number, b:string | number}{
return {a, b}
}
toObj("a","b");
toObj(1,2);
toObj(true,false) //불리언마저 추가하려면 함수 오버로딩이 너무 길어지게 된다...
//제네릭 사용시!
//함수 이름과 괄호 사이에 꺽쇠와 지정할 이름을 넣어준다.
function toObj<T>(a: T, b: T): {a:T, b:T}{
return {a, b}
}
//바깥에서 string타입을 넣어주면 toObj옆 꺽쇠<T>로 들어간다.
toObj<string>('A', 'B')
toObj<number>(1,2)
toObj<boolean>(true, false)
//제네릭 제약 사용시!
function toObj<T extends string | number | boolean>(a: T, b: T): {a:T, b:T}{
return {a, b}
}
toObj('A', 'B')
toObj(1,2)
toObj(true, false)
toObj(null,null) //타입 오류
또한 interface
나 타입 별칭등에서도 사용 가능하다.
interface ToObj<T>{
a: T,
b: T
}
function toObj<T extends string | number | boolean>(a: T, b: T): ToObj<T>{
return {a, b}
}
결국 꺽쇠로 흘러들어간 타입의 흐름이 어떻게 작동하는지 잘 알면 될것 같다!
제네릭 안에 여러속성(타입 변수)를 사용할 수도 있다.
//매개변수처럼 순서대로 들어온다!
interface User<T, U, V>{
name:T,
age:U,
isValid: V
}
const heropy: User<string, number, boolean> = {name: 'heropy', age:85, isValid:true};
const kim: User<string, number, boolean> = {name: 'kim', age:11, isValid:false};
const lee: User<string, number, boolean> = {name: 'lee', age:43, isValid:true};
//만약 튜플로도 받고싶다면...
type User<T,U,V> = { name: T, age:U, isVaolid: V} | [T, U, V];
//반복되는 타입 또한 타입 내부에 넣어버릴 수 있다.
type U = User<string, number, boolean>;
const evan: U = ["Evan", 77, false];
class Basket<T> {
public items: T[]
//바깥에서 T라는 변수를 주지않는다.
//타입 추론을 이용하여 T는 string이되고, string-array 타입이 된다.
//명시적으로 바깥에서 작성할 수도 있다.
constructor(...rest: T[]){
this.items = rest
}
putItem(item: T) {
this.items.unsfhit(item)
}
takeOutItems(count:number){
return this.items.splice(0, count)
}
}
const fruitsBasket = new Basket('apple', 'banana', 'cherry');
fruitsBasket.putItem('orange');
const fruits = fruitsBasket.takeOutItems(2);
console.log(fruits) // ['orange', apple']
console.log(fruitsBasket.items) // ['banana', 'cherry']
다만 이렇게 추론을 사용했을 경우 타입스크립트의 장점이 퇴색될수 있음.
명시적으로 지정해주는 편이 옳다.
이 부분은 고민거리구먼?
//타입을 지정할때 삼항연산자 사용 가능하다.
//string이나 number이면 boolean타입이고 아니면 never타입이다.
type MyType<T> = T extends string | number ? boolean : never;
const a: MyType<string> = true
const b: MyType<number> = true
const c: MyType<null> = true // never타입이기에 할당이 불가능하다.
개념 자체는 쉽지만 어떻게 활용할지 생각나질 않는다!
강사님께서 보여주셨다
type MyExclude<T,U> = T extends U ? never : T
type MyUnion = string | number | boolean | null
//string, number이 들어오면 할당해주고 boolean | null이 들어오면 never타입을 넣는다.
const a: MyExclude<MyUnion, boolean | null> = 123
사실 Exclude
유틸리티 타입은 이미 내장으로 존재한다!
방법은 쉬운데 용법이 이해가질 않는다. 다음을 잘 보자.
type IsPropertyType<T, U extends keyof T, V> = T[U] extends V ? true : false
type Keys = keyof User // 'name' | 'age'. 각각 키값 추출해서 유니온 타입을 만들어줌
interface User{
name: string
age: number
}
const n: IsPropertyType<User, 'name', number> = true
// infer의 기능은 I가 추론 가능한(infer)타입이면 참. 아니면 거짓.
type ArrayItemType<T> = T extends (inferI)[] ? I : never;
const numbers = [1,2,3];
const a: ArrayItemType<typeof numbers> = 123;
const b: ArrayItemType<boolean> = 123;
추론 가능한 타입이라면 할당해주는 것이구나?
실제로 써봐야 잘 알것같다.
type SecondArgumentType<t> = T extends (f: any, s: infer S) => any ? S : never
function hello(a: string, b: number){};
const a: SecondArgumentType<typeof hello> = 123;
//typeof hello는 (a: string, b: number) => void 타입을 반환.T라는 타입변수로 들어가서...
//두번째 매개변수를 추론한다. 따라서 숫자가 들어온다. 삼항연산자가 참으로 판단되어 S(숫자)타입이 할당된다.
Block, Element, Modifier로 나누어서 클래스를 작명하는 기법이다.
왜 나왔나?
=> 겹침 방지. 일관성. 보기 편해서..등등.
여러 규칙이있지만 대표적인 4가지만 소개해보겠음.
block__element_modname_modval
two__dash--style
: block-name__elem-name--mod-name--mod-val
-
하이픈으로 구분한다.__
이중밑줄로 block과 구분됨__
이중밑줄로 element와 구분됨-
하이픈으로 구분됨.camelCase
: blockName-elemName_modName_modVal
ReactStyle-Case
: BlockName-ElemName_modName_modVal
-
로 block과 구분됨이번 CSS과제에 적용할때 대충 훑어보고 적용했다...(반성반성...😥)
그래서 아예 공식문서에서 확인하였음!
그러면 block, element, modifier를 구분하는 방법은 뭘까?
웹 구성요소와 동일하다고 보면된다.
중첩 가능
블록은 내부에 블록을 포함할 수 있어야 한다
독립적
블록은 독립적인 개체기에 이동하여도 CSS나 JS의 수정이 필요없어야 한다.
재사용 가능
블록은 독립적인 개체이며 동시에 재사용이 가능하다.
컴포넌트의 단위와 비슷하다.
블록 외부에서 사용할 수 없는 블록의 구성품이다.
위치를 바꾸면 CSS의 수정이 필요하게되겠지?
또한 element내부에 element가 존재해선 안된다.
<!--
Correct. The structure of the full element name follows the pattern:
`block-name__element-name`
-->
<form class="search-form">
<div class="search-form__content">
<input class="search-form__input">
<button class="search-form__button">Search</button>
</div>
</form>
<!--
Incorrect. The structure of the full element name doesn't follow the pattern:
`block-name__element-name`
-->
<form class="search-form">
<div class="search-form__content">
<!-- Recommended: `search-form__input` or `search-form__content-input` -->
<input class="search-form__content__input">
<!-- Recommended: `search-form__button` or `search-form__content-button` -->
<button class="search-form__content__button">Search</button>
</div>
</form>
그러면 헤더를 좌,우로 나누었을때 header__left
보단 header-left
로 짓는게 더 낫겠구나?
같은 block이라도 modifier에 따라 다르게 보인다.
이는 선택사항. 마치 상태와 유사함.
뭐든 잘 알고써야한다. 규칙은 특히 그렇다. 잘 알고 쓰지 못하면 혼선만 가중할 뿐이다.
내일은 코드리뷰 받은걸 정리해야지...