class Greeter {
greet(name: string) {
console.log(`${name}, do your stuff!`);
}
}
// Ok
new Greeter().greet('Miss Frizzle');
// Error: Expected 1 arguments, but got 0
new Greeter().greet();
class Greeted {
constructor(message: string) {
console.log(`As I always say: ${message}`);
}
}
new Greeted('take chances, make mistakes, get messy');
// Error: Expected 1 arguments, but got 0
new Greeted();
class FieldTrip {
destination: string;
constructor(destination: string) {
this.destination = destination;
console.log(`We're going to ${this.destination}`);
// Error: Property 'nonexistent' does not exist on type 'FieldTrip'
this.nonexistent = destination;
}
}
const trip = new FieldTrip('planetarium');
// Ok
trip.destination;
// Error: Property 'nonexistent' does not exist on type 'FieldTrip'
trip.nonexistent;
class WithMethod {
// 메서드 접근 방식
myMethod() {}
}
new WithMethod().myMethod === new WithMethod().myMethod; // true
class WithProperty {
// 값이 속성인 함수
myProperty: () => {}
}
new WithProperty().myProperty === new WithProperty().myProperty // false
class WithPropertyParameters {
takesParameters = (input: boolean) => input ? 'Yes' : 'No';
}
const instance = new WithPropertyParametes();
// Ok
instance.takesParameters(true);
// Error: Argument of type 'number' is not assignable to parameter of type 'boolean'
instance.takesParameters(123);
undefined
타입으로 선언된 각 속성이 생성자에서 할당되었는지 확인합니다.class WithValue {
immediate = 0; // Ok
later: numebr; // Ok (constructor에서 할당)
mayBeUndefined: number | undefined; // Ok (undefined가 되는 것이 허용됨)
unused: number; // Error: Property 'ununsed' has no initializer
// and is not definitely assigned in the contructor
constructor() {
this.later = 1;
}
}
undefined
값에 접근할 수 있기 때문에 런라임 시 문제가 발생할 수 있습니다.class MissingInitializer {
property: string;
}
// (런타임시) TypeError: Cannot read property 'length' of undefined
new MissingInitializer().property.length;
!
를 추가해 검사를 비활성화하도록 설정합니다.class ActivitiesQueue {
// pending 속성이 initialize 메서드에 의해 생성자와 별도로 여러 번 초기화될 수 있기 때문에 ! assertion을 추가함
pending!: string[];
initialize(pending: string[]) {
this.pending = pending;
}
next() {
return this.pending.pop();
}
}
const activities = new ActivitiesQueue();
acitivities.initialze(['eat', 'sleep', 'learn']);
activivities.next();
?
를 추가해 선택적 속성을 선언할 수 있습니다.| undefined
를 포함하는 union 타입과 거의 동일하게 작동합니다.class MissingInitializer {
property?: string;
}
// Ok
new MissingInitializer().property?.length;
// Error: Object is possibly 'undefined'
new MissingInitializer().property.length;
readonly
키워드를 추가해 일기 전용 속성을 선언할 수 있습니다.readonly
키워드는 타입 시스템에만 존재하며 JavaScript로 컴파일할 때 삭제됩니다.readonly
키워드로 선언된 속성은 선언된 위치 또는 생성자에서 초깃값만 할당할 수 있습니다.class Quote {
readonly text: string;
constructor(text: string) {
this.text = text;
}
emphasize() {
// Error: Cannot assign to 'text' because it is a read-only property
this.text += '!';
}
}
const quote = new Quote('There is a brilliant child locked inside every student');
// Error: Cannot assign to 'text' because it is a read-only property
quote.text = 'hi';
readonly
로 선언된 속성은 값의 타입이 가능한 한 좁혀진 리터럴 타입으로 유추됩니다.class RandomQuote {
readonly explicit: string = 'Home is the nicest word there is.';
readonly implicit = 'Home is the nicest word there is.';
constructor() {
if (Math.random() > 0.5) {
this.explicit = "We start learning the minute we're born.";
// Error: type "We start learning the minute we're born."
// is not assinable to type 'Home is the nicest word there is.'
this.implicit = "We start learning the minute we're born.";
}
}
}
const quote = new RandomQoute();
quote.explicit; // 타입 : string
quote.implicit; // 타입 : 'Home is the nicest word there is.'
class Teacher {
sayHello() {
console.log('Take chances, make mistakes, get messy!');
}
}
let teacher: Teacher;
// Ok
teacher = new Teacher();
// Error: Type 'string' is not assignable to type 'Teacher'
teacher = 'Wahoo!';
class SchoolBus {
getAbilities() {
return ['magic', 'shapeshifting'];
}
}
function withSchoolBus(bus: SchoolBus) {
console.log(bus.getAbilities());
}
withSchoolBus({
getAbilities: () => ['transmogrification'],
});
withSchoolBus({
// Error: Type 'number' is not assignable to type 'string[]'
getAbilities: () => 123,
})
implements
키워드와 interface 이름을 추가함으로써 클래스의 해당 인스턴스가 interface를 준수한다고 선언할 수 있습니다.interface Learner {
name: string;
study(hours: number): void;
}
class Student implements Learner {
name: string;
constructor(name: string) {
this.name = name;
}
study(hours: number) {
for (let i = 0; i < hours; i++) {
console.log('...studing');
}
}
}
class Slacker implements Learner {
// Error: Class Slacker incorrectly implements interface 'Learner'
// Property 'study' is missing in type 'Slacker' but required in type 'Learner'
}
class Student implements Learner {
// Error: Member 'name' implicitly has an 'any' type
name;
// Error: parameter 'hours' implicitly has an 'any' type
study(hours);
}
interface Graded {
grades: number[];
}
interface Reporter {
report: () => string;
}
class ReportCard implements Graded, Reporter {
grades: number[];
constructor(grades: number[]) {
this.grades = grades;
}
report() {
return this.grades.join(', ');
}
}
class Empty implements Graded, Reporter {
// Error: Class 'Empty' incorrectly implements interface 'Grade'
// Property 'grades' is missing in type 'Empty' but required in type 'Graded'
// Error: Class 'Empty' incorrectly implements interface 'Reporter'
// Property 'report' is missing in type 'Empty' but required in type 'Reporter'
}
interface AgeIsNumber {
age: number;
}
interface AgeIsNotNumber {
age: () => string;
}
class AsNumber implements AgeIsNumber, AgeIsNotNumebr {
age = 0;
// Error: Property 'age' in type 'AsNumber' is not assignable to the same property in base type 'AgeIsNotNumber'
// Type 'number' is not assignable to type '() => string'
}
class Teacher {
teach() {
console.log('The surest test of discipline is its absence');
}
}
class StudentTeacher extends Teacher {
learn() {
console.log('I cannot afford the luxury of a closed mind');
}
}
const teacher = new StudentTeacher();
// Ok, 기본 클래스에서 정의됨
teacher.teach();
// Ok, 하위 클래스에서 정의됨
teacher.learn();
// Error: Property 'other' does not exist on type 'StudentTeacher'
teacher.other();
class Lesson {
subject: string;
constructor(subject: string) {
this.subject = subject;
}
}
class OnlineLesson extends Lesson {
url: string;
constructor(subject: string, url: string) {
super(subject);
this.url = url;
}
}
let lesson: Lesson;
lesson = new Lesson('coding'); // Ok
lesson = new OnlineLesson('coding', 'oreilly.com'); // Ok
let online: OnlineLession;
online = new OnlineLesson('coding', 'oreilly.com'); // Ok
online = new Lesson('coding'); // Error: Property 'url' is missing in type 'Lesson'
// but required in 'OnlineLession'
class PastGrades {
grades: number[] = [];
}
class LabeledPastGrades extends PastGrades {
label?: string;
}
let subClass: LabeledPastGrades;
subClass = new LabeledPastGrades(); // Ok
subClass = new PastGrades(); // Ok
super
키워드를 통해 기본 클래스 생성자를 호출해야 합니다.class GradeAnnouncer {
message: string;
constructor(grade: number) {
this.message = grade <= 65 ? 'Maybe next tiem...' : 'You pass';
}
}
class PassingAnnouncer extends GradeAnnouncer {
constructor() {
supper(100):
}
}
class FailingAnnouncer extends GradeAnnouncer {
constructor() {
// Error: Constructors for subclass must contain a 'super' call
}
}
super()
를 호출하기 전에 this
또는 super
에 접근하려는 경우 타입 오류를 보고합니다.class GradesTally {
grades: number[] = [];
addGrades(...grades: number[]) {
this.grades.push(...grades);
return this.grades.length;
}
}
class ContinuedGradesTally extends GradesTally {
constrctor(previousGrades: number[]) {
// Error: 'super' must be called before accessing
// 'this' in the constructor of a subclass
this.grades = [...previousGrades];
super();
// Ok
console.log('starting with length', this.grades.length);
}
}
class GradeCounter {
counterGrades(grades: string[], letter: string) {
return grades.filter(grade => grade === letter).length;
}
}
class FailureCounter extends GradeCounter {
countGrades(grades: string[]) {
return super.counterGrades(grades, 'F');
}
}
class AnyFailureChecker extends GradeCounter {
counterGrades(grades: string[]) {
// Error: Property 'counterGrades' in type 'AnyFailureChecker'
// is not assignable to the same property in base type 'GradeCounter'
// Type '(grades: string[]) => boolean' is not assignable to '(grades: string[], letter: string) => number'
// Type 'boolean' is not assignable to type 'number'
return super.counterGrades(grades, 'F') !== 0;
}
}
const counter : GradeCounter = new AnyFailureChecker();
// 예상 타입 : number
// 실제 타입 : boolean
const count = counter.counterGrades(['A', 'C', 'F']);
class Assignment {
grade?: number;
}
class GradedAssignment extends Assignment {
grade: number;
constructor(grade: number) {
super();
this.grade = grade;
}
}
class NumericGrade {
value = 0;
}
class VagueGrade extends NumericGrade {
value = Math.random() > 0.5 ? 1 : '...';
// Error: Property 'value' in type 'NumberOrString' is not
// assignable to the same property in base type 'JustNumber'
// Type 'string | number' is not assignable to type 'number'
// Type 'string' is not assignable to 'number'
}
const instance : NumericGrade = new VagueGrade();
// 예상 타입 : number
// 실제 타입 : number | string
instance.value;
abstact
키워드를 추가합니다.abstact class School {
readonly name: string;
constructor(name: string) {
this.name = name;
}
abstact getStudentTypes(): string[];
}
class Preschool extends School {
getStudentTypes() {
return ['preschooler'];
}
}
class Absence extends School {
// Error: Nonabstact class 'Absence' does not implement
// inherited abstact member 'getStudentTypes' from class 'School'
}
let school: School;
// Ok
school = new Preschool('Sunnyside Daycare');
// Error: Cannot create an instance of an abstact class
school = new Shool('somewhere else');
JavaScript는 클래스 멤버 이름 앞에 #
을 추가해 private 클래스 멤버임을 나타냅니다.
TypeScript는 private 클래스 멤버를 지원하지만, 타입 시스템에만 존재하는 클래스 메서드와 속성에 대해 조금 더 미묘한 프라이버시 정의 집합을 허용합니다.
TypeScript의 멤버 접근성은 클래스 멤버의 선언 이름 앞에 다음 키워드 중 하나를 추가해 만듭니다.
이러한 접근성 키워드는 순수하게 타입 시스템 내에 존재합니다.
class Base {
isPublicImplicit = 0;
public isPublicExplicit = 1;
protected isProtected = 2;
private isPrivate = 3;
#truePrivate = 4;
}
class SubClass extends Base {
example() {
// Ok
this.isPublicImplicit;
// Ok
this.isPublicExplicit;
// Ok
this.isProtected;
// Error: Property 'isPrivate' is private and only accessible within class 'Base'
this.isPrivate;
// Error: Property '#truePrivate' is not accessible outside class 'Base'
// because it has a private identifier
this.#truePrivate;
}
}
// Ok
new Subclass().isPublicImplicit;
// Ok
new Subclass().isPublicExplicit;
// Error: Property 'isProtected' is protected
// and only accessible witin class 'Base' and its subclasses.
new Subclass().isProtected;
// Error: Property 'isPrivate' is private
// and only accessible witin class 'Base'.
new Subclass().isPrivate;
class TwoKeywords {
private readonly name: string;
constructor() {
this.name = 'Anne Sullivan'; //Ok
}
log() {
console.log(this.name); // Ok
}
}
const two = new TwoKeywords();
// Error: Property 'name' is private and only accessible within class 'TwoKeyword'
// Cannot assign name because it is a read-only property
two.name = 'Savitribai Phule';
static
키워드를 사용해 클래스 자체에서 멤버를 선언합니다.static
키워드를 단독으로 사용하거나 readonly
와 접근성 키워드를 함께 사용할 수 있도록 지원합니다.static
, readonly
키워드 순으로 작성합니다.class Question {
protected static readonly answer: 'bash';
protected static readonly propmt =
"What's an orge's favorite programming language?";
guess(getAnswer: (prompt: string) => string) {
const answer = getAnswer(Question.propmt);
// Ok
if (answer === Question.answer) {
console.log('you got it');
} else {
console.log('Try again');
}
}
}
// Error: Property 'answer' is protected and only
// accessible within class 'Question' and subclasses.
Question.answer;
static
클래스 필드에 대해 readonly
와 접근성 제한자를 사용하면, 해당 필드가 해당 클래스 외부에서 접근되거나 수정되는 것을 제한하는 데 유용합니다.