คลาสแบบคงที่ TypeScript


146

ฉันต้องการย้ายไป TypeScript จาก JS แบบดั้งเดิมเพราะฉันชอบไวยากรณ์ที่คล้าย C # ปัญหาของฉันคือฉันไม่สามารถหาวิธีประกาศคลาสสแตติกใน TypeScript

ใน C # ฉันมักจะใช้คลาสคงที่เพื่อจัดระเบียบตัวแปรและวิธีการรวมเข้าด้วยกันในคลาสที่มีชื่อโดยไม่จำเป็นต้องติดตั้งวัตถุ ในวานิลลา JS ฉันเคยทำสิ่งนี้กับวัตถุ JS อย่างง่าย:

var myStaticClass = {
    property: 10,
    method: function(){}
}

ใน TypeScript ฉันอยากจะใช้แนวทาง C-sharpy ของฉัน แต่ดูเหมือนว่าคลาสแบบคงที่ไม่มีอยู่ใน TS วิธีแก้ไขปัญหาที่เหมาะสมสำหรับปัญหานี้คืออะไร?

คำตอบ:


166

TypeScript ไม่ใช่ C # ดังนั้นคุณไม่ควรคาดหวังแนวคิดเดียวกันของ C # ใน TypeScript อย่างแน่นอน คำถามคือทำไมคุณต้องการเรียนแบบคงที่?

ใน C # คลาสสแตติกเป็นคลาสที่ไม่สามารถคลาสย่อยและต้องมีเมธอดสแตติกเท่านั้น C # ไม่อนุญาตให้หนึ่งเพื่อกำหนดฟังก์ชั่นนอกชั้นเรียน ใน TypeScript นี้เป็นไปได้อย่างไรก็ตาม

หากคุณกำลังมองหาวิธีที่จะทำให้ฟังก์ชั่น / วิธีการของคุณใน namespace (เช่นไม่ใช่ระดับโลก) คุณสามารถพิจารณาใช้โมดูลของ TypeScript เช่น

module M {
    var s = "hello";
    export function f() {
        return s;
    }
}

เพื่อให้คุณสามารถเข้าถึง Mf () ภายนอก แต่ไม่ใช่ s และคุณไม่สามารถขยายโมดูลได้

ดูข้อกำหนดของ TypeScript สำหรับรายละเอียดเพิ่มเติม


ดังนั้นโมดูลสามารถมีวิธีการคงที่ แต่ชั้นเรียนไม่สามารถ? แต่โมดูลไม่สามารถมีข้อมูลคงที่ได้ มันไม่สะดวกเท่ากับ JS สำหรับการห่อข้อมูลและรหัสโดยไม่ต้องยกตัวอย่างอะไรเลย
dcsan

มันอาจจะมีประโยชน์ที่จะรวมถึงการที่คุณจะต้องรวมในของคุณ.js htmlดังนั้นAngular 2คุณอาจจะใช้System... so'd ทำSystem.import("Folder/M");(หรือสิ่งที่เส้นทางคือการรวบรวม.jsไฟล์) ก่อนbootstrap import
Serj เซแกน

11
เลิกใช้แล้ว tslint จะไม่อนุญาตให้คุณทำเช่นนั้นอีกต่อไปสำหรับโมดูลและเนมสเปซ อ่านที่นี่: palantir.github.io/tslint/rules/no-namespace
Florian

ฉันมีกรณีใช้สำหรับการเรียนแบบคงที่ ตอนนี้ฉันมีคลาสที่มีวิธีการคงที่เท่านั้น ในโครงการเราจำเป็นต้องจัดเตรียมการกำหนดค่าบางอย่างสำหรับแต่ละคลาสที่สามารถสร้างอินสแตนซ์ได้ การประกาศคลาสดังกล่าวเป็นแบบคงที่ไม่เพียงช่วยฉันสังเกตว่ามันจะไม่ถูกยกตัวอย่าง แต่ยังหลีกเลี่ยงนักพัฒนารายอื่นที่เพิ่มสมาชิกอินสแตนซ์
mdarefull

@llorian leitgeb เป็นวิธีที่ต้องการแล้วชั้นเรียนที่มีเพียงวิธีการคงที่และ / หรือคำหลักที่เป็นนามธรรม? ที่ดูเหมือนว่าเส็งเคร็งเมื่อเทียบกับโมดูลซึ่งตอนนี้ดูเหมือนว่าเลิก
Wired00

182

คลาสนามธรรมเป็นพลเมืองชั้นหนึ่งของ TypeScript ตั้งแต่ TypeScript 1.6 คุณไม่สามารถยกตัวอย่างคลาสนามธรรมได้

นี่คือตัวอย่าง:

export abstract class MyClass {         
    public static myProp = "Hello";

    public static doSomething(): string {
      return "World";
    }
}

const okay = MyClass.doSomething();

//const errors = new MyClass(); // Error

6
เมื่อจัดการกับคลาสสแตติกนี่คือการตอบสนองที่ดีที่สุดและฉันโหวตขึ้นนี้ Singletonใช้สำหรับรูปแบบหน่วยความจำแบบแบ่งใช้ของอินสแตนซ์คลาสเดียวกัน นอกจากนี้คลาสสแตติกไม่มีอินสแตนซ์ตามคำจำกัดความดังนั้นคุณต้องส่งข้อยกเว้นหากไคลเอ็นต์พยายามเริ่มต้น
loretoparisi

1
เราสามารถสร้างคลาสนามธรรมได้หรือไม่?
KimchiMan

@KimchiMan - ใช่abstractคำหลักได้รับการสนับสนุนใน TypeScript
เฟนตัน

1
อัปเดตเพื่อใช้abstract! @ KimchiMan - ความคิดที่ยอดเยี่ยม!
Obsidian

3
นี่เป็นวิธีที่ดีกว่าการทำเครื่องหมายคอนสตรัคเตอร์เป็นแบบส่วนตัวหรือไม่?
Joro Tenev

72

การนิยามคุณสมบัติสแตติกและเมธอดของคลาสอธิบายไว้ใน 8.2.1 ของSpecificationcript Language Specification :

class Point { 
  constructor(public x: number, public y: number) { } 
  public distance(p: Point) { 
    var dx = this.x - p.x; 
    var dy = this.y - p.y; 
    return Math.sqrt(dx * dx + dy * dy); 
  } 
  static origin = new Point(0, 0); 
  static distance(p1: Point, p2: Point) { 
    return p1.distance(p2); 
  } 
}

โดยที่Point.distance()เป็นวิธีคงที่ (หรือ "คลาส")


14
สิ่งนี้แสดงวิธีการสร้างวิธีการแบบคงที่แต่จะไม่ตอบคำถามที่เกี่ยวกับคลาสแบบสแตติก(เว้นแต่คำถามจริงจะเกี่ยวกับวิธีแบบสแตติก)
Marcus

18
ขอบคุณสำหรับความคิดเห็น มันอธิบายถึงวิธีการสร้างคุณสมบัติคงที่และวิธีการซึ่งนำมารวมกันช่วยให้หนึ่งในการสร้างชั้นเรียนที่มีข้อมูลและฟังก์ชั่นโดยไม่ต้องใช้การเริ่มต้น แม้ว่าจะไม่ใช่ "คลาสแบบคงที่" โดยเฉพาะ แต่จะทำตามข้อกำหนดดังที่อธิบายไว้ในตัวอย่าง JavaScript ของ OP
Rob Raisch

c # ไม่มีคลาสสแตติกจนถึงเวอร์ชัน 2 มีอยู่ใน c # เท่านั้นเพื่อป้องกันคุณสร้างอินสแตนซ์ คุณไม่สามารถทำเช่นนั้นด้วย javascript ดังนั้นจึงไม่สมเหตุสมผล
Simon_Weaver

4
@Simon_Weaver คุณไม่สามารถทำอะไรใน javascript? ป้องกันคลาสที่จะยกตัวอย่าง? typescript ไม่สนใจมากเกี่ยวกับ runtime อย่างไรก็ตามตราบใดที่คุณได้รับข้อผิดพลาดในการคอมไพล์เมื่อคุณพยายามทำสิ่งที่คุณไม่ได้รับอนุญาตนั่นคือทั้งหมดที่เราต้องการ
Alex

ฉันเดาว่าสิ่งที่ฉันหมายถึงคือ 'เราไม่ได้มีคำสำคัญคงที่ในระดับชั้นเรียน (ซึ่งหมายความว่าคุณไม่สามารถสร้างตัวอย่างของชั้นเรียน)' จนกระทั่งรุ่น 2 แต่การอ่านอีกครั้งฉันคิดว่าความคิดเห็นของฉันพลาด ชี้ไปเลย OP ไม่ได้มองหา 'คำหลักที่คงที่' สำหรับทั้งชั้นเรียน
Simon_Weaver

20

คำถามนี้ค่อนข้างเก่า แต่ฉันต้องการออกคำตอบที่ยกระดับภาษาปัจจุบัน น่าเสียดายที่คลาสสแตติกยังคงไม่มีอยู่ใน TypeScript แต่คุณสามารถเขียนคลาสที่มีลักษณะการทำงานคล้ายกับค่าใช้จ่ายเพียงเล็กน้อยโดยใช้ Constructor ส่วนตัวซึ่งป้องกันการสร้างอินสแตนซ์ของคลาสจากภายนอก

class MyStaticClass {
    public static readonly property: number = 42;
    public static myMethod(): void { /* ... */ }
    private constructor() { /* noop */ }
}

ตัวอย่างนี้จะช่วยให้คุณใช้คลาส "แบบคงที่" ซึ่งคล้ายกับสำเนา C # ที่มีข้อเสียเพียงอย่างเดียวที่ยังสามารถสร้างอินสแตนซ์ได้จากภายใน โชคดีที่คุณไม่สามารถขยายคลาสด้วยคอนสตรัคเตอร์ส่วนตัวได้


9

นี่คือวิธีหนึ่ง:

class SomeClass {
    private static myStaticVariable = "whatever";
    private static __static_ctor = (() => { /* do static constructor stuff :) */ })();
}

__static_ctorนี่คือการแสดงออกฟังก์ชั่นที่เรียกใช้ทันที typescript จะเอาท์พุทรหัสที่จะเรียกมันในตอนท้ายของชั้นเรียนที่สร้างขึ้น

อัปเดต: สำหรับประเภททั่วไปในตัวสร้างสแตติกซึ่งไม่ได้รับอนุญาตให้อ้างอิงโดยสมาชิกแบบสแตติกคุณจะต้องมีขั้นตอนเพิ่มเติมตอนนี้:

class SomeClass<T> {
    static myStaticVariable = "whatever";
    private ___static_ctor = (() => { var someClass:SomeClass<T> ; /* do static constructor stuff :) */ })();
    private static __static_ctor = SomeClass.prototype.___static_ctor();
}

แน่นอนว่าในกรณีใด ๆ คุณสามารถเรียกใช้ตัวสร้างสแตติกแบบทั่วไปหลังจากคลาสได้เช่น:

class SomeClass<T> {
    static myStaticVariable = "whatever";
    private __static_ctor = (() => { var example: SomeClass<T>; /* do static constructor stuff :) */ })();
}
SomeClass.prototype.__static_ctor();

เพียงจำไว้ว่าอย่าใช้thisใน__static_ctorด้านบน (ชัด)


วิธีนี้ยังคงปล่อยตัวสร้างสำหรับคลาส
พวกเขาเรียกความทรงจำ

1
"วิธีนี้" เป็นแฮ็คและไม่เปลี่ยนการทำงานปกติของคอมไพเลอร์ตามที่คาดไว้ แม้จะclass SomeClass {}สร้างนวกรรมิก - แทบจะไม่คุ้มที่จะแสดงความคิดเห็นราวกับว่ามีการนำเสนอปัญหาใหม่ ;) FYI: - ฟังก์ชั่นเดียวที่มี "คนนี้" newเมื่อเรียกบนวัตถุหรือผ่านทางไม่มีจริง สิ่งนี้มีอยู่ไม่ว่าจะเป็น "คลาส" ใด ๆ ก็ตาม
James Wilkins

6

ฉันได้รับกรณีการใช้งานแบบเดียวกันในวันนี้ (31 ต.ค. 2014) และพบว่านี่เป็นวิธีแก้ปัญหา มันขึ้นอยู่กับการวิจัยของฉันและมันได้ผลสำหรับฉัน ความคาดหวัง - เพื่อให้บรรลุดังต่อไปนี้ใน TypeScript:

var myStaticClass = {
    property: 10,
    method: function(){} 
}

ฉันทำอย่างนี้:

//MyStaticMembers.ts
namespace MyStaticMembers {
        class MyStaticClass {
           static property: number = 10;
           static myMethod() {...}
        }
        export function Property(): number {
           return MyStaticClass.property;
        }
        export function Method(): void {
           return MyStaticClass.myMethod();
        }
     }

ดังนั้นเราจะกินมันดังต่อไปนี้:

//app.ts
/// <reference path="MyStaticMembers.ts" />
    console.log(MyStaticMembers.Property);
    MyStaticMembers.Method();

สิ่งนี้ใช้ได้สำหรับฉัน หากใครมีข้อเสนอแนะที่ดีกว่าอื่น ๆ โปรดให้พวกเราทุกคนได้ยิน !!! ขอบคุณ ...


3

คลาสแบบคงที่ในภาษาเช่น C # มีอยู่เนื่องจากไม่มีโครงสร้างระดับบนสุดอื่น ๆ ในการจัดกลุ่มข้อมูลและฟังก์ชั่น อย่างไรก็ตามใน JavaScript พวกเขาทำและมันก็เป็นธรรมชาติมากกว่าที่จะประกาศสิ่งที่คุณทำ หากต้องการจำลองไวยากรณ์ของคลาสอย่างใกล้ชิดยิ่งขึ้นคุณสามารถประกาศเมธอดดังนี้:

const myStaticClass = {
    property: 10,

    method() {

    }
}

1
วิธีการนี้ไม่ทำให้เวิร์กโฟลว์ของคุณยุ่งเหยิงใช่หรือไม่ ในอีกด้านหนึ่งคุณใช้คลาสและอินสแตนซ์และทันใดนั้นคุณก็เริ่มประกาศข้อมูลในออบเจ็กต์ JS ปกติอีกครั้ง ... การใช้คลาสสแตติกยังช่วยให้มีความพร้อมใช้งานไม่เพียงเกี่ยวกับการทำงานภายในของภาษา
Kokodoko

4
ฉันไม่พบว่ามันยุ่งกับเวิร์กโฟลว์ของฉัน; ในทางตรงกันข้ามฉันคิดว่ามันสะดวกในการใช้วัตถุ JavaScrpit ที่ไม่มีคลาสหรือเพียงแค่ฟังก์ชั่นธรรมดาและค่าคงที่ในโมดูล อย่างไรก็ตามฉันยังพยายามหลีกเลี่ยงสถานะโลกให้มากที่สุดเท่าที่จะทำได้ดังนั้นฉันจึงไม่ค่อยต้องการอะไรเช่นตัวแปรระดับคงที่
Yogu

1

ดู http://www.basarat.com/2013/04/typescript-static-constructors-for.html

นี่คือวิธีการ 'ปลอม' ตัวสร้างแบบคงที่ มันไม่ได้โดยไม่มีอันตรายของมัน - ดูรายการ CodePlex อ้างอิง

class Test {
    static foo = "orig";

    // Non void static function
    static stat() {
        console.log("Do any static construction here");
        foo = "static initialized";
        // Required to make function non void
        return null;
    }
    // Static variable assignment
    static statrun = Test.stat();
}

// Static construction will have been done:
console.log(Test.foo);

1

วิธีหนึ่งที่เป็นไปได้ในการบรรลุเป้าหมายนี้คือมีอินสแตนซ์คงที่ของคลาสภายในคลาสอื่น ตัวอย่างเช่น:

class SystemParams
{
  pageWidth:  number = 8270;
  pageHeight: number = 11690;  
}

class DocLevelParams
{
  totalPages: number = 0;
}

class Wrapper
{ 
  static System: SystemParams = new SystemParams();
  static DocLevel: DocLevelParams = new DocLevelParams();
}

จากนั้นพารามิเตอร์สามารถเข้าถึงได้โดยใช้ Wrapper โดยไม่ต้องประกาศอินสแตนซ์ของมัน ตัวอย่างเช่น:

Wrapper.System.pageWidth = 1234;
Wrapper.DocLevel.totalPages = 10;

ดังนั้นคุณจะได้รับประโยชน์ของวัตถุประเภท JavaScript (ตามที่อธิบายไว้ในคำถามเดิม) แต่ด้วยข้อดีของความสามารถในการเพิ่มการพิมพ์ของ TypeScript นอกจากนี้ยังหลีกเลี่ยงการเพิ่ม 'คงที่' ด้านหน้าพารามิเตอร์ทั้งหมดในชั้นเรียน


1

ด้วยโมดูลภายนอก ES6 สามารถทำได้ดังนี้:

// privately scoped array
let arr = [];

export let ArrayModule = {
    add: x => arr.push(x),
    print: () => console.log(arr),
}

สิ่งนี้จะป้องกันการใช้งานโมดูลภายในและเนมสเปซซึ่งถือว่าเป็นการปฏิบัติที่ไม่ดีโดย TSLint [1] [2]ช่วยให้การกำหนดขอบเขตส่วนตัวและสาธารณะและป้องกันการเริ่มต้นของวัตถุคลาสที่ไม่พึงประสงค์


0

ฉันกำลังค้นหาสิ่งที่คล้ายกันและได้รับสิ่งที่เรียกว่า Singleton Patternฉันกำลังมองหาบางสิ่งบางอย่างที่คล้ายกันและบางสิ่งบางอย่างมาข้ามเรียกว่า

การอ้างอิง: รูปแบบซิงเกิล

ฉันกำลังทำงานกับคลาส BulkLoader เพื่อโหลดไฟล์ประเภทต่างๆและต้องการใช้รูปแบบ Singleton สำหรับมัน วิธีนี้ฉันสามารถโหลดไฟล์จากคลาสแอปพลิเคชันหลักของฉันและเรียกคืนไฟล์ที่โหลดได้อย่างง่ายดายจากคลาสอื่น

ด้านล่างเป็นตัวอย่างง่ายๆว่าคุณสามารถสร้างตัวจัดการคะแนนสำหรับเกมที่มีรูปแบบ TypeScript และรูปแบบซิงเกิลได้อย่างไร

คลาส SingletonClass {

private static _instance:SingletonClass = new SingletonClass();

private _score:number = 0;

constructor() {
    if(SingletonClass._instance){
        throw new Error("Error: Instantiation failed: Use SingletonDemo.getInstance() instead of new.");
    }
    SingletonClass._instance = this;
}

public static getInstance():SingletonClass
{
    return SingletonClass._instance;
}

public setScore(value:number):void
{
    this._score = value;
}

public getScore():number
{
    return this._score;
}

public addPoints(value:number):void
{
    this._score += value;
}

public removePoints(value:number):void
{
    this._score -= value;
}   }

จากนั้นทุกที่ในชั้นเรียนอื่น ๆ ของคุณคุณจะสามารถเข้าถึง Singleton โดย:

var scoreManager = SingletonClass.getInstance();
scoreManager.setScore(10); scoreManager.addPoints(1);
scoreManager.removePoints(2); console.log( scoreManager.getScore() );

0

คุณยังสามารถใช้คำหลักnamespaceเพื่อจัดระเบียบตัวแปรคลาสวิธีการและอื่น ๆ ดูเอกสาร

namespace Validation {
    export interface StringValidator {
        isAcceptable(s: string): boolean;
    }

    const lettersRegexp = /^[A-Za-z]+$/;
    const numberRegexp = /^[0-9]+$/;

    export class LettersOnlyValidator implements StringValidator {
        isAcceptable(s: string) {
            return lettersRegexp.test(s);
        }
    }

    export class ZipCodeValidator implements StringValidator {
        isAcceptable(s: string) {
            return s.length === 5 && numberRegexp.test(s);
        }
    }
}

ดูความคิดเห็นของ Florian ด้านบน "นี่เลิกใช้แล้วและ tslint จะไม่อนุญาตให้คุณทำเช่นนั้นสำหรับโมดูลและเนมสเปซอีกต่อไปอ่านที่นี่: palantir.github.io/tslint/rules/no-namespace"
reggaeguitar
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.