반응형
Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- React #effect hook #useEffect
- react #useCallback #react Hook
- rate limit
- 빡킹독
- #useRef #언제 쓰는데?
- react fragment
- React-Query
- npm #not being able to find a file #npm install Error
- RateLimit
- 노마드 코더 #타입스크립트 #typescript #class
- 코드스테이츠 #알고리즘 #그리디
- React #Hook rules #Hook 규칙
- raect typescript #react #typescript #styled-component
- react
- 버블링 #갭쳐링 #이벤트 #JS
- 다익스트라 #파티 #백준
- React #controlled component #비제어 컴포넌트 #제어 컴포넌트
- React #리액트 이벤트 주기 #리액트 이벤트
- 플로이드 #c++
- 이친수
- interceptors
- 얕은 복사 #깊은 복사 #shallow copy #deep copy
- useState #Hooks
- 백준 #적록색약
- 백준 #직각삼각형
- axios
- html entities
- DP #c++
- donwstream #upstream #origin
- JWT #토큰 #refreshToken #accessToken #Token #token #localStorage #sessionStorage
Archives
- Today
- Total
꿈꾸는 개발자
8장 클래스 본문
8.1 클래스 메서드
- 매개변수 기본 타입: any
- 독립 함수와 동일한 방식으로 이해
- 클래스 생성자(constructor) 또한 동일한 취급
//메서드
class Greeter{
greet(name:string){
console.log(`${name}`);
}
}
new Greeter().greet('haha'); //ok
new Greeter().greet(); //Error: 인수 필요!
=================================================
//생성자
class Greeter{
constructor(mess:string){
console.log(`${mess}`);
}
}
new Greeter("haha");
new Greeter() //Error 매개변수 제공 안 해서
8.2 클래스 속성
- TS 클래스 속성 읽기/쓰기 하려면 명시적으로 선언
- 속성 뒤 선택적 타입 애너테이션 가능
- TS도 생성자 내의 할당에 대해 그 멤버 클래스에 존재하는 멤버인지 추론X ⇒ 의문(추론하지 않는다면서 nonexistent는 왜 에러)?
class FieldTrip{
//멤버 변수 => 멤버 변수가 없다면 this.des=des도 에러
des:string;
constructor(des:string){
this.des=des;
this.nonexistent=des; //type FieldTrip에 존재X
//클래스 속성 nonexistent를 선언X => 할당 허용X
}
}
===========================================
class FieldTrip{
//멤버 변수로만 선언해도 에러 사라짐
des:string;
nonexistent:string;
constructor(des:string){
this.des=des;
this.nonexistent=des;
}
}
8.2.1 함수 속성(???)
- 두 가지 함수 선언 방식 (JS)
- myFunction(){} 형식 ⇒ 클래스 prototype에 할당(인스턴스 당 동일한 함수 정의 사용)
- 함수 속성 선언 방식 ⇒ 인스턴스 당 새로운 함수 생성
- 함수 속성: 독립 함수와 동일한 구문 사용 가능!
8.2.2 초기화 검사class WithPropParam{ takesParam=(input:boolean)=> input? "Yes":"No"; } const instance=new WithPropParam(); instance.takesParam(true); instance.takesParam(123); //Error type 에러 발생
- strict compiler environment (undefined 체크)
- 기본적으로 값을 초기화하지 않으면 undefined이 됨!
class Value{
a=0; //즉시 할당 가능
b:number; //constructor()에서 할당해야 함
c:number|undefined;
d:number; //여기만 에러 발생 strict compile환경에서 undefined이 들어가 있어서
constructor(){
this.b=1;
}
}
- strictNullcheck을 해제하면 에러 사라짐 ⇒ 이럴 경우 JS 런타임에서 에러가 발생할 수 있음 ().property.length의 경우 (undefined의 length 측정 불가) ⇒ BUT, TS에선 문제 없이 넘어갈 것이다.
확실하게 할당된 속성
- 엄격한 속성이 적용 안되는 속성인 경우 이름 뒤에 “!”을 추가!(검사 비활성화) ⇒undefined 할당
- ! 어서션을 사용하는 대신 리펙터링을 통해 코드 개선을 추천
8.2.3 선택적 속성
class MissingInitial{
property?:string; //strict 검사 적용X
}
new MissingInitial().property?.length //ok;
new MissingInitial().property.length //Error: Object is possible undefined
8.2.4 읽기 전용 속성
- 속성 이름 앞에 readonly를 추가해 속성을 읽기 전용으로 선언 ⇒ 컴파일 후 삭제
- 선언된 위치에서 초깃값/생성자에서 값만 할당 가능 그 외는 불가!
class ReadOnly{
readonly text:string;
constructor(){
this.text="";
}
}
- 진정한 읽기 전용을 실천하기 위해선 #private/get()함수 속성을 고려해라!
- 원식 타입을 갖는 readonly 속성 ⇒ 리터럴 타입으로 유추됨
class Random{
readonly explicit:string="aaaaa";
readonly implicit="bbbbb" //더 구체적인 값(리터럴 값)
constructor(){
if(Math.random()>0.5){
this.explicit="cccc";
this.implicit="dddd";//에러 발생 리터럴 값이기 때문에
}
}
}
const quote=new Random();
quote.explicit;//타입 string
quote.implicit;//"bbbbb"
8.3 타입으로서의 클래스
- TS ⇒ 클래스 선언(런타임 값) + 타입 애너테이션 모두 생성 (정확히 무슨 의미)
class Teacher{
sayHello(){
console.log("aaaaa");
}
}
let teacher:Teacher;
teacher=new Teacher();//ok
teacher="AAA"; //Error: string => type Teacher assign불가
- TS는 클랫의 동일한 멤버를 모두 포함하는 모든 객체 타입을 클래스에 할당할 수 있는 것으로 간주 ⇒ 구조적 타이핑 방식X(객체 형태만 고려하기 때문) ⇒구조적 타이핑도 객체를 고려하는 것 아님??집합의 관계를 통해?
class Bus{
getAb(){
return ["a","b "];
}
}
//클래스를 매개변수로 받음
function withBus(bus:Bus){
console.log(bus.getAb());
}
withBus(new Bus);
withBus({
getAb:()=>["c"]
})
withBus({
getAb:()=>123,//Type number =>string[] 할당 불가
})
- 대부분 클래스 타입 요청 위치에 객체 값 전달X
8.4 클래스와 인터페이스
- 클래스 뒤에 implements 키워드 + 인터페이스 이름 클래스의 인스턴스가 추가한 인터페이스를 준수한다고 선언할 수 있음
interface Learner{
name:string;
study(hours:number):void;
}
class Student implements Learner{
name:string;
constructor(name:string){
this.name=name;
}
study(hours:number){ //만약 타입 애너테이션을 하지 않으면 any으로 간주함
for(let i=0;i<hours;i++){
console.log("studying");
}
}
}
class slacker implements Learner{
//property를 선언하지 않아 에러 발생
}
- 인터페이스 멤버를 함수로 선언하기 위해 메서드 구문 사용
- 인터페이스 구현 목적: 안정성 때문 ⇒ 타입 검사기로 신호를 보냄!
8.4.1 다중 인터페이스 구현
- TS 클래스는 다중 인터페이스 구현 ⇒ 선언 가능
interface Graded{
grades:number[];
}
interface Reporter{
report:()=>string;
}
class ReprotCard implements Graded, Reporter{
grades:number[];
constructor(grades:number[]){
this.grades=grades;
}
report(){
return this.grades.join(",");
}
}
class Empty implements Graded,Reporter {}
//Error: property grades 및 report를 작성하지 않아 에러 발생!
- 출동하는 인터페이스를 구현하는 클래스 선언하면 ⇒ 타입 오루 발생
8.5 클래스 확장
- TS 기존 JS 개념에 타입 검사를 추가함
- 기존 클래스의 메서드 + 속성 ⇒ 파생 클래스에서 사용이 가능
- 없는 속성/메서드 접근시 에러 발생
8.5.1 할당 가능성 확장
class A{
propA:string;
constructor(propA:string){
this.propA=propA;
}
}
class B extends A{
B:string;
constructor(A:string,B:string){
super(A);
this.B=B;
}
}
let a:A;
a=new A("coding"); //당연히 ok
a=new B("coding","haha"); //ok
let b:B;
b=new B("coding","haha") //ok
b= new A("coding") //Error:property "B" is missing type in A
- JAVA/C++과 동일한 작동 원리 ⇒ 집합 관계로 이해
- 하지만, TS에선 추가적으로 구조적 타이핑으로 인해 하위 클래스의 모든 멤버가 동일하다면 ⇒ 기본 클래스 인스턴스를 사용할 수 있다.
- 자주 발생하지는 않음!
8.5.2 재정의된 생성자
- JS처럼 하위 클래스는 자체 생성자 정의 필요X ⇒암묵적으로 기본 클래스 생성자 사용
- 생성할 경우: ⇒ super를 통한 기본 클래스 생성자 호출(이 때 타입 검사기는 매개변수가 올바르게 선언됐는지 확인)
- 하위 클래스: 기본 클래스와 상관없이 모든 매개변수 선언 가능!
class A{
propA:string;
constructor(grade:number){
this.propA=grade>=65?"aaa":"bbbb";
}
}
class B extends A{
constructor(){{ //올바르게 선언하면 문제X
super(100);
}}
}
class C extends A{
constructor(){} //기본 생성자를 호출하지 않으면 오류 발생
}
- 생성자에서 this를 사용하기 전 반드시 super()를 호출해야 함 ⇒ this를 먼저 사용할 경우 에러 발생!
8.5.3 재정의된 메서드
- 하위 클래스의 메서드가 기본 클래스의 메서드에 할당될 수 있는 경우 ⇒ 동일한 이름으로 새 메서드 재선언 가능(포함 관계이기 때문에 메서드 타입도 기본 메서드 대신 사용할 수 있어야 함)
- 다른 언어처럼 매개변수 추가 등의 Overriding은 허용하지 않는 것인가?
- https://www.typescriptlang.org/docs/handbook/2/classes.html (공식문서에 있는 Overriding Methods 부분 참고)
- 밑 코드를 살펴보면, class B는 A를 상속 받고, 내부 methodA에 새로운 매개변수인 name?:string이 추가됨을 볼 수 있다 ⇒ 이럴 경우 TS 자체에선 Base class의 구조가 어긋나지 않았다고 판단 매개변수의 추가를 허용해준다.
- 하지만 ?이 사라지는 순간 에러가 발생함!
- Overloading 자체는 허용하지 않는듯?(검색해도 잘 안 나옴)
- 다른 언어처럼 매개변수 추가 등의 Overriding은 허용하지 않는 것인가?
class A{
methodA(grades:string[],letter:string){
return grades.filter(grade=>grade===letter).length;
}
}
class B extends A{
methodA(grades:string[],letter:string,name?:string){ //선택 매개변수일 경우 사용이 가능한듯!
if(name===undefined){
return super.methodA(grades,"f");
}else{
console.log(name);
return super.methodA(grades,"f");
}
}
}
class C extends A{
methodA(grades:string[]){ //Error; 기본 클래스(A)의 반환값이 number type인데
//boolean typed을 반환해서 에러 발생
return super.methodA(grades,"F")!==0;
}
}
const counter:A=new C();//애초에 C가 에러이기 때문에 에러 발생
//에상 타입:number;
//실제 타입: boolean;
const count =counter.methodA(["A","B","C"])
8.5.4 재정의된 속성
- 위와 마찬가지로 속성에도 동일한 원리가 적용된다. 메서드와 마찬가지로 기본 클래스와 구조적으로 일치해야 함
- 보통 하위 클래스에선 해당 속성을 유니언 타입의 더 구체적인 하위 집합/클래스 속성 타입에서 확장되는 타입으로 만듦⇒ 확장된 타입이란 의미가 모호함(밑의 경우 class B에 grade: number | string으로 할 경우 에러가 발생함(무조건 기본 클래스를 따라야 하는 것처럼 보이는데??) ⇒ 네로잉의 경우는 문제 없어 보임! (다른 언어의 overriding의 개념과의 차이점을 분석해 볼 것!)
class A{
grade?:number; //number | undefined으로 선언? 이것과는 다르지 않나??
}
class B extends A{
grade:number;
constructor(grade:number){
super();
this.grade=grade; //여기에선 항상 존재하는 number type으로 선언함!
}
}
- 속성 유니언 타입의 허용된 값 집합을 확장 할 수 없음 ⇒ 할 경우 하위 클래스 속성은 기본 클래스 속성 타입에 할당 불가!
class A{
value=0;
}
class B extends A{
value=Math.random()>0.5? 1: "...."; //TypeError: type number | string X =>to type number
}
cosnt instance:A=new B();
//예상 한 타입: number
//실제 타입: number | string
instance.value;
8.6 추상 클래스
- 하위 클래스가 일부 메서드를 만들 것을 예상하고 기본 클래스를 구현하는 방법도 있음
- abstract 키워드를 class명 앞에 추가함! ⇒ 인터페이스와 동일한 방식으로 선언됨!
abstract class A{
readonly name:string;
constructor(name:string){
this.name=name;
}
abstract getType():string[];
}
class B extends A{
getType(){
return ["AAA"];
}
}
class C extends A {} //추상 클래스를 상속했으면 무조건 메서드를 선언/구현해야 함!
- 추상화 클래스는 직접 인스턴스 생성 불가X
- 추상화 클래스의 경우 세부 사항이 채워질 것이란 예상되는 프레임워크에 자주 사용됨
8.7 멤버 접근성
- JS에선 멤버 이름에 #을 추가해서 private 클래스임을 나타냄 (프라이버시 강화)
- TS는 타입 시스템에만 존재하는 클래스 메서드와 속성에 대해 조금 더 미묘한 프라이버시 정의 집합을 허용함 (클래스 멤버의 선언 이름 앞에 키워드를 추가함)⇒ 컴파일 되면 사라짐(private만 런타임에도 존재함)
- public:(기본값) 모든 곳 누구나 접근 가능
- protected: 클래스 내부 또는 하위 클래스에서만 접근 가능
- private: 클래스 내부에서만 접근 가능
class Base{
isA=0;
public a=1;
protected b=2;
private c=3
#truePrivate=4;
}
class Subclass extends Base{
exmaples(){
//접근 가능한 멤버 속성들
this.isA;
this.a
this.b
}
//접근 불가능 한 멤버 속성
this.c;
this.#truePrivate;
}
//protected로 선언돼서 외부 접근 불가 Base + subclass 내부에서만 접근 가능
new Subclass().b
//private은 당연히 안됨
new Subclass().c
<aside> 💡 private readonly name: string 형태와 같이 사용할 수 있음
</aside>
8.7.1 정적 필드 제한자
- JS에선 static 키워드를 사용해 클래스 자체에 멤버를 선언함
- static (단독)/ + readonly 같이 사용 가능
- 순서: 접근성 키워드 ⇒ static ⇒ readonly
- static (단독)/ + readonly 같이 사용 가능
class A{
protected static readonly a:"base";
protected static readonly prompt="asdfasfd";
guess(getAnswer:(prompt:string)=>string){
const answer=getAnswer(A.prompt);
}
if(answer===A.a){
console.log("yes");
}else{
console.log("No");
}
}
A.a //protected라서 안됨!
Reference:
- 조시 골드버그, 러닝 타입스크립트, 고승원 옮김, 2023
- Typescript Handbook