जेनेरिक क्लास में टाइप पैरामीटर से एक नई वस्तु बनाएं


145

मैं अपने सामान्य वर्ग में एक प्रकार के पैरामीटर की एक नई वस्तु बनाने की कोशिश कर रहा हूं। मेरी कक्षा में View, मेरे पास सामान्य प्रकार की वस्तुओं की 2 सूचियाँ हैं जो कि प्रकार के मापदंडों के रूप में पारित की जाती हैं, लेकिन जब मैं बनाने की कोशिश करता हूं new TGridView(), तो टाइपस्क्रिप्ट कहता है:

प्रतीक नहीं मिला 'TGridView

यह कोड है:

module AppFW {
    // Represents a view
    export class View<TFormView extends FormView, TGridView extends GridView> {
        // The list of forms 
        public Forms: { [idForm: string]: TFormView; } = {};

        // The list of grids
        public Grids: { [idForm: string]: TGridView; } = {};

        public AddForm(formElement: HTMLFormElement, dataModel: any, submitFunction?: (e: SubmitFormViewEvent) => boolean): FormView {
            var newForm: TFormView = new TFormView(formElement, dataModel, submitFunction);
            this.Forms[formElement.id] = newForm;
            return newForm;
        }

        public AddGrid(element: HTMLDivElement, gridOptions: any): GridView {
            var newGrid: TGridView = new TGridView(element, gridOptions);
            this.Grids[element.id] = newGrid;
            return newGrid;
        }
    }
}

क्या मैं सामान्य प्रकार से ऑब्जेक्ट बना सकता हूं?

जवाबों:


96

क्योंकि संकलित जावास्क्रिप्ट में सभी प्रकार की जानकारी मिटा दी गई है, आप Tकिसी ऑब्जेक्ट को नया करने के लिए उपयोग नहीं कर सकते ।

आप इसे गैर-जेनेरिक तरीके से कंस्ट्रक्टर में टाइप करके पास कर सकते हैं।

class TestOne {
    hi() {
        alert('Hi');
    }
}

class TestTwo {
    constructor(private testType) {

    }
    getNew() {
        return new this.testType();
    }
}

var test = new TestTwo(TestOne);

var example = test.getNew();
example.hi();

प्रकारों को कसने के लिए आप जेनरिक का उपयोग करके इस उदाहरण का विस्तार कर सकते हैं:

class TestBase {
    hi() {
        alert('Hi from base');
    }
}

class TestSub extends TestBase {
    hi() {
        alert('Hi from sub');
    }
}

class TestTwo<T extends TestBase> {
    constructor(private testType: new () => T) {
    }

    getNew() : T {
        return new this.testType();
    }
}

//var test = new TestTwo<TestBase>(TestBase);
var test = new TestTwo<TestSub>(TestSub);

var example = test.getNew();
example.hi();

144

जेनेरिक कोड के भीतर एक नई वस्तु बनाने के लिए, आपको इसके निर्माता फ़ंक्शन द्वारा प्रकार को संदर्भित करने की आवश्यकता है। इसलिए इसे लिखने के बजाय:

function activatorNotWorking<T extends IActivatable>(type: T): T {
    return new T(); // compile error could not find symbol T
}

आपको यह लिखना होगा:

function activator<T extends IActivatable>(type: { new(): T ;} ): T {
    return new type();
}

var classA: ClassA = activator(ClassA);

इस प्रश्न को देखें: कक्षा तर्क के साथ सामान्य प्रकार का आविष्कार


35
कुछ देर से (लेकिन उपयोगी) टिप्पणी - अगर आपका कंस्ट्रक्टर आड़े आता है तो new()ज़रूरतों को भी - या अधिक सामान्य:type: {new(...args : any[]): T ;}
रिसोचेट

1
@Yoda IActivatable एक स्व-निर्मित इंटरफ़ेस है, अन्य प्रश्न देखें: stackoverflow.com/questions/24677592/…
Jamie

1
यह कैसे पता लगाया जाना चाहिए { new(): T ;}। मुझे इस भाग को सीखने में कठिन समय लग रहा है।
abitcode

1
यह (यहां और अन्य सभी समाधान) आपको जेनेरिक को निर्दिष्ट करने के लिए कक्षा में पास करने की आवश्यकता है। यह हैडी और बेमानी है। इसका मतलब है कि अगर मेरा कोई आधार वर्ग है ClassA<T>और इसका विस्तार करना है ClassB extends ClassA<MyClass>, तो मुझे भी new () => T = MyClassइनमें से कोई भी समाधान नहीं करना होगा।
एंड्रयूजेनमिन

@AndrewBenjamin आपका सुझाव फिर क्या है? कुछ भी शुरू करने की कोशिश नहीं कर रहा है (वास्तव में सीखना चाहता है) लेकिन इसे हैकी और निरर्थक कहते हुए आप एक और तरीका जानते हैं जो आप साझा नहीं कर रहे हैं :)
पेरी

37

सभी प्रकार की जानकारी जावास्क्रिप्ट पक्ष में मिटा दी जाती है और इसलिए आप @Sohnee राज्यों की तरह टी को नया नहीं कर सकते, लेकिन मैं निर्माण में पारित टाइप किए गए पैरामीटर को प्राथमिकता देना चाहूंगा:

class A {
}

class B<T> {
    Prop: T;
    constructor(TCreator: { new (): T; }) {
        this.Prop = new TCreator();
    }
}

var test = new B<A>(A);

12
export abstract class formBase<T> extends baseClass {

  protected item = {} as T;
}

इसकी वस्तु किसी भी पैरामीटर को प्राप्त करने में सक्षम होगी, हालांकि, टाइप टी केवल एक टाइपस्क्रिप्ट संदर्भ है और एक निर्माता के माध्यम से नहीं बनाया जा सकता है। यही है, यह किसी भी वर्ग की वस्तुओं का निर्माण नहीं करेगा।


6
खबरदार , यह काम कर सकता है लेकिन परिणामी वस्तु प्रकार की नहीं होगी T, इसलिए हो सकता है कि कोई भी गेटर्स / सेटर Tकाम में न आए ।
डैनियल ऑर्नामो

@ DanielOrmeño समझाने की परवाह है? उससे तुम्हारा क्या मतलब है? मेरा कोड इस स्निपेट का उपयोग करके काम करता प्रतीत होता है। धन्यवाद।
eestein

जावास्क्रिप्ट एक व्याख्या की गई भाषा है और परमानंद के पास अभी तक प्रकारों या वर्गों के संदर्भ नहीं हैं। तो T टाइपस्क्रिप्ट के लिए सिर्फ एक संदर्भ है।
जेल्स कार्डो

यह एक गलत जवाब है, यहां तक ​​कि जावास्क्रिप्ट में भी। यदि आपके पास एक है class Foo{ method() { return true; }, तो टी को {}} कास्टिंग आपको यह विधि नहीं देगा!
JCKödel

9

मुझे पता है कि देर से ही सही लेकिन @ तडसपा के जवाब को इस्तेमाल करके थोड़ा समायोजित किया जा सकता है

TCreator: new() => T

के बजाय

TCreator: { new (): T; }

तो परिणाम इस तरह दिखना चाहिए

class A {
}

class B<T> {
    Prop: T;
    constructor(TCreator: new() => T) {
        this.Prop = new TCreator();
    }
}

var test = new B<A>(A);

4

मैं एक बेस क्लास के भीतर से जेनेरिक को इंस्टेंट करने की कोशिश कर रहा था। उपर्युक्त उदाहरणों में से किसी ने भी मेरे लिए काम नहीं किया क्योंकि उन्हें कारखाने के तरीके को कॉल करने के लिए एक ठोस प्रकार की आवश्यकता थी।

इस पर थोड़ी देर के लिए शोध करने और ऑनलाइन समाधान खोजने में असमर्थ होने के बाद, मैंने पाया कि यह काम करता है।

 protected activeRow: T = {} as T;

टुकड़े:

 activeRow: T = {} <-- activeRow now equals a new object...

...

 as T; <-- As the type I specified. 

सभी एक साथ

 export abstract class GridRowEditDialogBase<T extends DataRow> extends DialogBase{ 
      protected activeRow: T = {} as T;
 }

कहा कि, यदि आपको एक वास्तविक उदाहरण की आवश्यकता है, जिसका आपको उपयोग करना चाहिए:

export function getInstance<T extends Object>(type: (new (...args: any[]) => T), ...args: any[]): T {
      return new type(...args);
}


export class Foo {
  bar() {
    console.log("Hello World")
  }
}
getInstance(Foo).bar();

यदि आपके पास तर्क हैं, तो आप उपयोग कर सकते हैं।

export class Foo2 {
  constructor(public arg1: string, public arg2: number) {

  }

  bar() {
    console.log(this.arg1);
    console.log(this.arg2);
  }
}
getInstance(Foo, "Hello World", 2).bar();

2
यह एक गलत जवाब है, यहां तक ​​कि जावास्क्रिप्ट में भी। यदि आपके पास एक वर्ग फू {विधि () {वापसी सच है; }, टी को {} कास्टिंग करना आपको यह विधि नहीं देगा!
JCKödel

अगर आपके पास तरीके नहीं हैं तो मैं काम करता हूं। यदि आपके पास एक विधि है, तो आपको उन विधियों का उपयोग करना चाहिए जो मैंने अभी-अभी जोड़े गए कोड का उपयोग किया है।
क्रिश्चियन पैटजर

2

यह वही है जो मैं टाइप जानकारी को बनाए रखने के लिए करता हूं:

class Helper {
   public static createRaw<T>(TCreator: { new (): T; }, data: any): T
   {
     return Object.assign(new TCreator(), data);
   }
   public static create<T>(TCreator: { new (): T; }, data: T): T
   {
      return this.createRaw(TCreator, data);
   }
}

...

it('create helper', () => {
    class A {
        public data: string;
    }
    class B {
        public data: string;
        public getData(): string {
            return this.data;
        }
    }
    var str = "foobar";

    var a1 = Helper.create<A>(A, {data: str});
    expect(a1 instanceof A).toBeTruthy();
    expect(a1.data).toBe(str);

    var a2 = Helper.create(A, {data: str});
    expect(a2 instanceof A).toBeTruthy();
    expect(a2.data).toBe(str);

    var b1 = Helper.createRaw(B, {data: str});
    expect(b1 instanceof B).toBeTruthy();
    expect(b1.data).toBe(str);
    expect(b1.getData()).toBe(str);

});

1

मैं इसका उपयोग let instance = <T>{}; करता हूं: यह आम तौर पर 1 EDIT काम करता है:

export class EntityCollection<T extends { id: number }>{
  mutable: EditableEntity<T>[] = [];
  immutable: T[] = [];
  edit(index: number) {
    this.mutable[index].entity = Object.assign(<T>{}, this.immutable[index]);
  }
}

5
यह वास्तव में एक नए उदाहरण का Tसंकेत नहीं देगा, यह सिर्फ एक खाली वस्तु बना देगा जो टाइपस्क्रिप्ट सोचता है कि प्रकार का है T। आप अपने उत्तर को थोड़ा और विस्तार से बताना चाह सकते हैं।
सैंडी जिफोर्ड

मैंने कहा कि यह आम तौर पर काम करता है। जैसा आप कहते हैं वैसा ही है। कंपाइलर कोड़ा नहीं है और आपके पास जेनेरिक कोड है। आप इस उदाहरण का उपयोग मैन्युअल रूप से उन गुणों को सेट करने के लिए कर सकते हैं जिन्हें आप स्पष्ट निर्माता की आवश्यकता के बिना कर सकते हैं।
बोजो

1

इस प्रश्न का उत्तर नहीं दे रहा है, लेकिन, इस तरह की समस्याओं के लिए एक अच्छा पुस्तकालय है: https://github.com/typestack/class-transformer (हालांकि यह सामान्य प्रकारों के लिए काम नहीं करेगा, क्योंकि वे वास्तव में मौजूद नहीं हैं रन-टाइम पर (यहां सभी काम कक्षा के नाम (जो कि क्लास कंस्ट्रक्टर हैं) के साथ किया जाता है)

उदाहरण के लिए:

import {Type, plainToClass, deserialize} from "class-transformer";

export class Foo
{
    @Type(Bar)
    public nestedClass: Bar;

    public someVar: string;

    public someMethod(): string
    {
        return this.nestedClass.someVar + this.someVar;
    }
}

export class Bar
{
    public someVar: string;
}

const json = '{"someVar": "a", "nestedClass": {"someVar": "B"}}';
const optionA = plainToClass(Foo, JSON.parse(json));
const optionB = deserialize(Foo, json);

optionA.someMethod(); // works
optionB.someMethod(); // works

1

मुझे पार्टी के लिए देर हो रही है लेकिन यह तरीका है जिससे मुझे काम करना है। सरणियों के लिए हमें कुछ तरकीबें चाहिए:

   public clone<T>(sourceObj: T): T {
      var cloneObj: T = {} as T;
      for (var key in sourceObj) {
         if (sourceObj[key] instanceof Array) {
            if (sourceObj[key]) {
               // create an empty value first
               let str: string = '{"' + key + '" : ""}';
               Object.assign(cloneObj, JSON.parse(str))
               // update with the real value
               cloneObj[key] = sourceObj[key];
            } else {
               Object.assign(cloneObj, [])
            }
         } else if (typeof sourceObj[key] === "object") {
            cloneObj[key] = this.clone(sourceObj[key]);
         } else {
            if (cloneObj.hasOwnProperty(key)) {
               cloneObj[key] = sourceObj[key];
            } else { // insert the property
               // need create a JSON to use the 'key' as its value
               let str: string = '{"' + key + '" : "' + sourceObj[key] + '"}';
               // insert the new field
               Object.assign(cloneObj, JSON.parse(str))
            }
         }
      }
      return cloneObj;
   }

इसे इस तरह उपयोग करें:

  let newObj: SomeClass = clone<SomeClass>(someClassObj);

इसमें सुधार किया जा सकता है लेकिन मेरी जरूरतों के लिए काम किया गया है!


1

मैं इसे अनुरोध से जोड़ रहा हूं, इसलिए नहीं कि मुझे लगता है कि यह सीधे प्रश्न को हल करता है। मेरे समाधान में मेरे SQL डेटाबेस से तालिकाओं को प्रदर्शित करने के लिए एक तालिका घटक शामिल है:

export class TableComponent<T> {

    public Data: T[] = [];

    public constructor(
        protected type: new (value: Partial<T>) => T
    ) { }

    protected insertRow(value: Partial<T>): void {
        let row: T = new this.type(value);
        this.Data.push(row);
    }
}

इसका उपयोग करने के लिए, मान लें कि मेरे पास मेरे डेटाबेस VW_MyData में एक दृश्य (या तालिका) है और मैं एक क्वेरी से लौटे प्रत्येक प्रविष्टि के लिए अपने VW_MyData वर्ग के निर्माता को हिट करना चाहता हूं:

export class MyDataComponent extends TableComponent<VW_MyData> {

    public constructor(protected service: DataService) {
        super(VW_MyData);
        this.query();
    }

    protected query(): void {
        this.service.post(...).subscribe((json: VW_MyData[]) => {
            for (let item of json) {
                this.insertRow(item);
            }
        }
    }
}

कारण यह है कि डेटा को लौटाए गए मान को निर्दिष्ट करने के लिए वांछनीय है, कहते हैं कि मेरे पास कुछ कोड है जो इसके निर्माण में VW_MyData के कुछ कॉलम में परिवर्तन लागू करता है:

export class VW_MyData {
    
    public RawColumn: string;
    public TransformedColumn: string;


    public constructor(init?: Partial<VW_MyData>) {
        Object.assign(this, init);
        this.TransformedColumn = this.transform(this.RawColumn);
    }

    protected transform(input: string): string {
        return `Transformation of ${input}!`;
    }
}

यह मुझे टाइपस्क्रिप्ट में आने वाले मेरे सभी डेटा पर रूपांतरण, सत्यापन, और जो कुछ भी करने की अनुमति देता है। उम्मीद है कि यह किसी के लिए कुछ अंतर्दृष्टि प्रदान करता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.