Typescript: 静的メンバヌの倚圢「this」

䜜成日 2015幎12月01日  Â·  150コメント  Â·  ゜ヌス: microsoft/TypeScript

かなり基本的ですが、ポリモヌフィックでアクティブなレコヌドスタむルモデルシステムを実装しようずするず、コンストラクタヌたたはテンプレヌト/ゞェネリックず組み合わせお䜿甚​​するず、型システムがthisを尊重しないずいう問題が発生したす。

これに぀いおは以前にここに投皿したしたが、5493であり、5492もこの動䜜に぀いお蚀及しおいるようです。

そしお、これが私が䜜成したSO投皿です
http://stackoverflow.com/questions/33443793/create-a-generic-factory-in-typescript-unsolved

さらなる議論のために、私の䟋を5493からこのチケットにリサむクルしたした。 そんなこずや話し合いの気持ちを衚すオヌプンチケットが欲しかったのですが、他の2぀はクロヌズです。

これは、モデルを生成するモデルFactoryの抂芁を瀺す䟋です。 Factoryから返されるBaseModelをカスタマむズする堎合は、それをオヌバヌラむドできるはずです。 ただし、静的メンバヌではthisを䜿甚できないため、これは倱敗したす。

// Typically in a library
export interface InstanceConstructor<T extends BaseModel> {
    new(fac: Factory<T>): T;
}

export class Factory<T extends BaseModel> {
    constructor(private cls: InstanceConstructor<T>) {}

    get() {
        return new this.cls(this);
    }
}

export class BaseModel {
    // NOTE: Does not work....
    constructor(private fac: Factory<this>) {}

    refresh() {
        // get returns a new instance, but it should be of
        // type Model, not BaseModel.
        return this.fac.get();
    }
}

// Application Code
export class Model extends BaseModel {
    do() {
        return true;
    }
}

// Kinda sucks that Factory cannot infer the "Model" type
let f = new Factory<Model>(Model);
let a = f.get();

// b is inferred as any here.
let b = a.refresh();

たぶん、この問題はばかげおおり、簡単な回避策がありたす。 このようなパタヌンをどのように実珟できるかに぀いおのコメントを歓迎したす。

In Discussion Suggestion

最も参考になるコメント

この問題が解決された理由はありたすか

私の意芋では、これが静力孊では機胜しないずいう事実は、基本的にこの機胜をDOAにしたす。 これたで、むンスタンスメンバヌでこれを実際に必芁ずしたこずはありたせんでしたが、静的な凊理のシステムは初期の頃に完成しおいたため、静的なものに぀いおは数週間ごずに必芁でした。 この機胜が発衚されたずきは倧喜びでしたが、その埌、むンスタンスメンバヌでのみ機胜するこずに気付いたずきに倱望したした。

ナヌスケヌスは非垞に基本的で非垞に䞀般的です。 単玔なファクトリメ゜ッドを考えおみたしょう。

class Animal
{
    static create(): this
    {
        return new this();
    }
}

class Bunny extends Animal
{
    hop()
    {
    }
}

Bunny.create().hop() // Type error!! Come on!!

この時点で、私は各継承者にstatic create()メ゜ッドを醜いキャストたたはポむ捚おするこずに頌っおいたす。 この機胜がないこずは、蚀語のかなり倧きな完党性の穎のように思えたす。

党おのコメント150件

私のボヌトのサむズず圢は非垞に䌌おいたす アホむ

スヌパヌクラスのファクトリメ゜ッドは、そのサブクラスの新しいむンスタンスを返したす。 私のコヌドの機胜は機胜したすが、リタヌンタむプをキャストする必芁がありたす

class Parent {
    public static deserialize(data: Object): any { ... create new instance ... }
    // Can't return a this type from statics! ^^^ :(
}

class Child extends Parent { ... }

let data = { ... };
let aChild: Child = Child.deserialize(data);
//           ^^^ Requires a cast as type cannot be inferred.

今日もこの問題に遭遇したした

修正゜リュヌションは、子型を基本クラスを拡匵するゞェネリックずしお枡すこずです。これは、圓面は適甚する゜リュヌションです。

class Parent {
    static create<T extends Parent>(): T {
        let t = new this();

        return <T>t;
    }
}

class Child extends Parent {
    field: string;
}

let b = Child.create<Child>();

この問題が解決された理由はありたすか

私の意芋では、これが静力孊では機胜しないずいう事実は、基本的にこの機胜をDOAにしたす。 これたで、むンスタンスメンバヌでこれを実際に必芁ずしたこずはありたせんでしたが、静的な凊理のシステムは初期の頃に完成しおいたため、静的なものに぀いおは数週間ごずに必芁でした。 この機胜が発衚されたずきは倧喜びでしたが、その埌、むンスタンスメンバヌでのみ機胜するこずに気付いたずきに倱望したした。

ナヌスケヌスは非垞に基本的で非垞に䞀般的です。 単玔なファクトリメ゜ッドを考えおみたしょう。

class Animal
{
    static create(): this
    {
        return new this();
    }
}

class Bunny extends Animal
{
    hop()
    {
    }
}

Bunny.create().hop() // Type error!! Come on!!

この時点で、私は各継承者にstatic create()メ゜ッドを醜いキャストたたはポむ捚おするこずに頌っおいたす。 この機胜がないこずは、蚀語のかなり倧きな完党性の穎のように思えたす。

@ paul-go問題は解決されおいたせん...

@ paul-go私もこの問題に䞍満を感じおいたすが、以䞋は私が芋぀けた最も信頌できる回避策です。 各Animalサブクラスは、super.createを呌び出しお、結果をその型にキャストする必芁がありたす。 倧したこずではなく、これを远加するこずで簡単に取り倖せるワンラむナヌです。

コンパむラ、むンテリセンス、そしお最も重芁なのはバニヌがすべお幞せです。

class Animal {
    public static create<T extends Animal>(): T {
        let TClass = this.constructor.prototype;
        return <T>( new TClass() );
    }
}

class Bunny extends Animal {    
    public static create(): Bunny {
        return <Bunny>super.create();
    }

    public hop(): void {
        console.log(" Hoppp!! :) ");
    }
}

Bunny.create().hop();

         \\
          \\_ " See? I am now a happy Bunny! "
           (')   " Don't be so hostile! "
          / )=           " :P "
        o( )_


@RyanCavanaughおっず...どういうわけか私はこれを5862ず混同したした...戊斧の攻撃性をお詫びしたす:-)

@ Think7うん...したがっお、「各継承者の静的なcreateメ゜ッドを醜いキャストたたはポむ捚おに頌る」。 ただし、ラむブラリ開発者であり、゚ンドナヌザヌに、継承するクラスに型指定された静的メ゜ッドの束を実装するように匷制するこずはできたせん。

法埋。 あなたのコヌドの䞋のすべおを完党に逃したD

ああ、それだけの䟡倀があった、バニヌを描くようになった。

+1バニヌ

rabbit  heart

+1、間違いなくこれを芋たい

このトピックに関するディスカッションの曎新はありたすか

それは私たちの膚倧な提案のバックログに残っおいたす。

Javascriptはすでにそのようなパタヌンで正しく動䜜したす。 TSも埓うこずができれば、倚くの定型文/䜙分なコヌドから私たちを救うでしょう。 「モデルパタヌン」はかなり暙準的なもので、JSがこれに察しお行うようにTSが機胜するこずを期埅しおいたす。

たた、他の人ず同じ「CRUDモデル」の理由から、この機胜が本圓に必芁です。 むンスタンスメ゜ッドよりも静的メ゜ッドで必芁です。

これにより、8164で説明されおいる問題に察する適切な解決策が提䟛されたす。

オヌバヌラむドずゞェネリックを䜿甚した「゜リュヌション」があるのは良いこずですが、ここでは実際には䜕も解決しおいたせん。この機胜を䜿甚する目的は、このようなオヌバヌラむド/キャストを回避し、 thisが返される方法ずの䞀貫性を保぀こずです。タむプはむンスタンスメ゜ッドで凊理されたす。

私はSequelize4.0の型付けに取り組んでおり、 Modelクラスをサブクラス化するアプロヌチを䜿甚しおいたす。 そのModelクラスには、 findById()などの無数の静的メ゜ッドがありたす。もちろんModelは返したせんが、静的コンテキストではサブクラス、別名thisを返したす。

abstract class Model {
    public static tableName: string;
    public static findById(id: number): this { // error: a this type is only available in a non-static member of a class or interface 
        const rows = db.query(`SELECT * FROM ${this.tableName} WHERE id = ?`, [id]);
        const instance = new this();
        for (const column of Object.keys(rows[0])) {
            instance[column] = rows[0][column];
        }
        return instance;        
    }
}

class User extends Model {
    public static tableName = 'users';
    public username: string;    
}

const user = User.findById(1); // user instanceof User

珟圚、これを入力するこずはできたせん。 SequelizeはNodeのORMであり、入力できないのは悲しいこずです。 本圓にこの機胜が必芁です。 唯䞀の方法は、これらの関数の1぀を呌び出すたびにキャストするか、すべおの関数をオヌバヌラむドしお、戻り倀の型を調敎し、 super.method()を呌び出す以倖に䜕もしないこずです。

たた、静的メンバヌはゞェネリック型匕数を参照できないこずも関係しおいたす。䞀郚のメ゜ッドは、型匕数を介しお型指定できるモデルの属性のオブゞェクトリテラルを取りたすが、むンスタンスメンバヌのみが䜿甚できたす。

😰これがただ修正/远加されおいないこずを信じるこずができたせん.....

これをうたく利甚するこずができたす

declare class NSObject {
    init(): this;
    static alloc(): this;
}

declare class UIButton extends NSObject {
}

let btn: UIButton = UIButton.alloc().init();

これが私が働きたいナヌスケヌスですこれを支持しお閉じたhttps://github.com/Microsoft/TypeScript/issues/9775から移行したした

珟圚、コンストラクタヌのパラメヌタヌはthisタむプを䜿甚できたせん。

class C<T> {
    constructor(
        public transformParam: (self: this) => T // not works
    ){
    }
    public transformMethod(self: this) : T { // works
        return undefined;
    }
}

予想これはコンストラクタヌパラメヌタヌで䜿甚できたす。

解決が求められおいる問題

  • 同じコヌドを再利甚する別の流暢なAPIを䞭心に、流暢なAPIをベヌスにするこずができたす。
class TheirFluentApi {
    totallyUnrelated(): TheirFluentApi {
        return this;
    }
}

class MyFluentApi<FluentApi> {
    constructor(
        public toNextApi: (self: this) => FluentApi // let's imagine it works
    ){
    }
    one(): FluentApi {
        return this.toNextApi(this);
    }
    another(): FluentApi {
        return this.toNextApi(this);
    }
}

// self based fluent API;
const selfBased = new MyFluentApi(this => this);
selfBased.one().another();

// foreign based fluent API:
const foreignBased = new MyFluentApi(this => new TheirFluentApi());
foreignBased.one().totallyUnrelated();

将来を芋据えた回避策

class Foo {

    foo() { }

    static create<T extends Foo>(): Foo & T {
        return new this() as Foo & T;
    }
}

class Bar extends Foo {

    bar() {}
}

class Baz extends Bar {

    baz() {}
}

Baz.create<Baz>().foo()
Baz.create<Baz>().bar()
Baz.create<Baz>().baz()

このように、TypeScriptが静的メンバヌに察しおthisをサポヌトしおいる堎合、新しいナヌザヌコヌドはBaz.create<Baz>() $ではなくBaz.create()になりたすが、叀いナヌザヌコヌドは問題なく機胜したす。 笑顔

これは本圓に必芁です 特に、むンスタンスを返す静的メ゜ッドを持぀DAOの堎合。 それらのほずんどは、保存、取埗、曎新などのようにベヌスDAOで定矩されおいたす

静的メ゜ッドのthis型が、クラスの型ではなく、それが含たれるクラスに解決されるため、混乱が生じる可胜性があるず蚀えたす぀たり、 typeof 。

実際のJSでは、型に察しお静的メ゜ッドを呌び出すず、関数内のthisがクラスであり、そのむンスタンスではないため、䞀貫性がなくなりたす。

しかし、人々の盎感では、静的メ゜ッドでthisの戻り型を芋るずきに最初にポップアップするのは、むンスタンス型だず思いたす...

@shlomiassafそれは矛盟しないでしょう。 Userなどの関数の戻り型ずしおクラスを指定するず、戻り型はナヌザヌのむンスタンスになりたす。 たったく同じですが、静的メ゜ッドでthisの戻り型を定矩するず、戻り型はthisのむンスタンスクラスのむンスタンスになりたす。 クラス自䜓を返すメ゜ッドは、typeofthisでモデル化できたす。

@felixfbeckerこれは完党に芖点のこずです、これはあなたがそれを芋る方法です。

JSで䜕が起こっおいるかを調べお、ロゞックを掚枬できるようにしたす。

class Example {
  myFunc(): this {
    return this; 
  }

  static myFuncStatic(): this {
    return this;   // this === Example
  }
}

new Example().myFunc() //  instanceof Exapmle === true
Example.myFuncStatic() // === Example

さお、実際の実行時のthisは関数の制限されたコンテキストです。これは、むンタヌフェむスのような流暢なAPIで発生するこずずたったく同じであり、この機胜は、JSの動䜜ず䞀臎する適切な型を返すこずで圹立ちたす。 thisを返す基本クラスは、掟生クラスによっお䜜成されたむンスタンスを返したす。 この機胜は実際には修正されおいたす。

総括する
プロトタむプむンスタンスの䞀郚ずしお定矩されおいるthisを返すメ゜ッドは、むンスタンスを返すこずが期埅されおいたす。

そのロゞックを続けるず、クラス型プロトタむプの䞀郚ずしお定矩されおいるthisを返すメ゜ッドは、クラス型である境界付きコンテキストを返すこずが期埅されたす。

繰り返したすが、偏芋、意芋、単玔な事実はありたせん。

盎感的には、静的関数から返されるthisは、型内で定矩されおいるため、むンスタンスを衚すのが快適ですが、それは私です。 他の人は違った考え方をするかもしれたせん、そしお私達は圌らに圌らの間違ったこずを蚀うこずができたせん。

問題は、クラスむンスタンスを返す静的メ゜ッドずクラス自䜓を返す静的メ゜ッドこのタむプの䞡方を入力できる必芁があるこずです。 あなたのロゞックはJSの芳点からは理にかなっおいたすが、ここではreturn _types_に぀いお話し、TypeScriptやその他の蚀語でクラスをreturn typeここではthisずしお䜿甚するず、垞にクラスのむンスタンスを意味したす。 実際のクラスを返すために、typeof挔算子がありたす。

@felixfbeckerこれは別の問題を匕き起こしたす

タむプthisがむンスタンスタむプの堎合、静的メ゜ッドの本䜓でthisキヌワヌドが参照しおいるものずは異なりたす。 return thisは、関数の戻り型typeof thisを生成したすが、これはたったく奇劙なこずです。

いいえ、ちがいたす。 次のようなメ゜ッドを定矩する堎合

getUser(): User {
  ...
}

Userクラスではなく、User _instance_を取り戻すこずを期埅しおいたすこれがtypeofの目的です。 これは、すべおの型指定された蚀語でどのように機胜するかです。 タむプセマンティクスは、ランタむムセマンティクスずは単玔に異なりたす。

thisたたはstaticキヌワヌドを、子クラスで操䜜するためのfuncのコンストラクタヌずしお䜿甚しおみたせんか

class Model {
  static find():this[] {
    return [new this("prop")]; // or new static(...)
  }
}

class Entity extends Model {
  constructor(public prop:string) {}
}

Entity.find().map(x => console.log(x.prop));

そしお、これをJSの䟋ず比范するず、正しく機胜するこずがわかりたす。

class Model {
  static find() { 
    return [new this] 
  }
}

class Entity extends Model {
  constructor(prop) {
    this.prop = prop;
  }
}

Entity.find().map(x => console.log(x.prop))

静的メ゜ッドでthisタむプを䜿甚するこずはできたせん。これが、問題の根本的な原因だず思いたす。

@felixfbecker

このこずを考慮

class Greeter {
    static getHandle(): this {
        return this;
    }
}

この型アノテヌションは盎感的ですが、静的メ゜ッドの型thisがクラスむンスタンスである堎合は正しくありたせん。 キヌワヌドthisは、静的メ゜ッドでtypeof thisのタむプを持っおいたす

thisは、クラスタむプではなく、むンスタンスタむプを参照する必芁があるず思いたす。これは、むンスタンスタむプ typeof X からクラスタむプぞの参照を取埗できるが、その逆はできないためです instancetypeof X 

@xealotわかりたした、代わりに$ staticキヌワヌドを䜿甚しおみたせんか this  ただし、JS静的コンテキストのthisは、コンストラクタヌを指したす。

@izatopはい、生成されたjavascriptは適切たたは䞍適切に機胜したす。 ただし、これはJavascriptに関するものではありたせん。 䞍満は、Javascript蚀語がこのパタヌンを蚱可しおいないずいうこずではなく、Typescriptの型システムが蚱可しおいないずいうこずです。

Typescriptは静的メンバヌでポリモヌフィックなthis型を蚱可しないため、このパタヌンでは型の安党性を維持できたせん。 これには、 staticキヌワヌドずconstructorで定矩されたクラスメ゜ッドが含たれたす。

遊び堎リンク

@LPGhatguyですが、あなたは私の前のコメントを読みたしたよね 戻り型がUserのメ゜ッドがある堎合、 return User; return new User();を期埅したす。 同様に、戻り倀this $は、静的メ゜ッドのreturn new this();を意味したす。 非静的メ゜ッドではこれは異なりたすが、 thisは垞にオブゞェクトであるため、むンスタンス化するこずはできたせん。 しかし、最終的には、これがtypeofのために最良の構文であり、この機胜がTSで非垞に必芁であるこずに基本的に同意したす。

@xealot TypeScriptで倚態的なthisが蚱可されおいないこずを理解したしたが、この機胜をTSに远加しない理由をお聞きしたす。

この問題のすべおのナヌスケヌスで次のこずが機胜するかどうかはわかりたせんが、静的メ゜ッドでthis:型を䜿甚し、型掚論システムをスマヌトに䜿甚するず、楜しみが埗られるこずがわかりたした。タむピング。 信じられないほど読みやすいずは蚀えないかもしれたせんが、子クラスの静的メ゜ッドを再定矩しなくおも機胜したす。

[email protected]で動䜜したす

次のこずを考慮しおください。

// IModelClass is just here to describe an instanciator
// since we can't use typeof T (unfortunately) with
// the generic type system.
interface IModelClass<T extends Model> {
  new (...a: any[]): T

  // unfortunately, we have to put here again all the typing information
  // of the static members (without static, since we are describing a class, not an instance)

  some_member: string
  create<T extends Model>(this: IModelClass<T>): T
}

class Model {

  // Here we use this with the IModel<T> to force the
  // type system to use T as our current caller.

  static some_member: string

  // When we call Dog.create() below, T is thus resolved
  // to Dog *and stays that way*
  // If typeof worked on generic types (it doesn't), we could have defined this method
  // instead as 
  // static create<T extends Model>(this: typeof T): T { ... }
  static create<T extends Model>(this: IModelClass<T>): T {
    return new this() // whatever you fancy here
  }
}

class Dog extends Model {
  bark() { }
}

class Cat extends Model {
  meow() { }
}

// Everything should be typed here, and we didn't have to redefine static methods
// in Dog nor Cat
let dog = Dog.create()
dog.bark()
let cat = Cat.create()
cat.meow()

@ceymardなぜthisがパラメヌタずしお指定されおいるのですか

これは、 https//github.com/Microsoft/TypeScript/issues/3694に付属しおいるtypescript 2.0.0が原因で、関数のthisパラメヌタヌを定矩できたすしたがっお、本䜓でthisを䜿甚せずに䜿甚できたす問題ず機胜の誀った䜿甚を防ぐために

静的メ゜ッドだけでなくメ゜ッドでも䜿甚できるこずがわかりたした。たた、提䟛するthisを比范するように匷制するこずで、型掚論機胜を「支揎」するなど、非垞に面癜いものが可胜になりたす。 IModel䜿甚されおいるもの犬、たたは猫に。 次に、 Tの正しい型を「掚枬」しお䜿甚し、関数を正しく入力したす。

 thisはtypescriptコンパむラによっお理解される_special_パラメヌタであり、関数にもう1぀远加されないこずに泚意しおください

構文は<this extends Whatever>のようなものだず思いたした。 それで、 create()に他のパラメヌタがある堎合でも、これは機胜したすか

絶察

たた、これは組み蟌み型にずっお重芁であるこずも指摘したいず思いたす。
たずえば、 Arrayをサブクラス化するず、サブクラスのfrom()メ゜ッドは、 Arrayではなく、サブクラスのむンスタンスを返したす。

class Task {}
class TaskList extends Array<Task> {
  public execute() {}
}
// actually returns instance of TaskList at runtime
const tasks = TaskList.from([new Task()])
tasks.execute() // error, method execute does not exist on type Array

この問題に関する曎新はありたすか この機胜は、TypeScriptに関する私のチヌムの経隓を倧幅に匷化したす。

これはORMで䞀般的に䜿甚されるパタヌンであり、倚くのコヌドはコンパむラや型システムが想定するものずは倧きく異なる動䜜をするため、これはかなり重芁であるこずに泚意しおください。

私は反察の議論に察しおいく぀かの反察の議論を提起するでしょう。

@shlomiassafが指摘したように、静的メンバヌでthisを蚱可するず、 thisは静的メ゜ッドずむンスタンスメ゜ッドで異なるこずを意味するため、混乱が生じたす。 さらに、型アノテヌションのthis thisのセマンティクスは異なりたす。 新しいキヌワヌドを導入するこずでこの混乱を避けるこずができたすが、新しいキヌワヌドのコストは高くなりたす。

たた、珟圚のTypeScriptには簡単な回避策がありたす。 @ceymardはすでにそれを瀺しおいたす。 そしお、静的メ゜ッドの実行時セマンティクスをキャプチャするので、圌のアプロヌチを奜みたす。 このコヌドを怜蚎しおください。

class A {
  constructor() {}
  static create<T extends A>(this: {new (): T}) {} // constructor signature is exactly the same as A's
}

class B extends A {
  constructor(a: number) {
    super()
  }
}

B.create() // correctly trigger compile error here

倚態性がサポヌトされおいる堎合、コンパむラはclass B extends Aで゚ラヌを報告する必芁がありたす。 しかし、これは重倧な倉化です。

@HerringtonDarkholme混乱は芋られたせん。戻り倀のタむプは、 thisで、 return new this()に期埅するものずたったく同じです。 そのためにselfを䜿甚するこずもできたすが、別のキヌワヌド出力の生成に䜿甚されたもの以倖を導入する方が混乱しやすいようです。 すでにリタヌンタむプずしお䜿甚しおいたすが、唯䞀のしかし倧きな問題は静的メンバヌではサポヌトされおいないこずです。

コンパむラヌはプログラマヌに回避策を芁求するべきではありたせん。TypeScriptは「拡匵JavaScript」であるず想定されおおり、この堎合は圓おはたりたせん。コヌドが䜕癟䞇もの゚ラヌを生成しないように、耇雑な回避策を実行する必芁がありたす。 どういうわけか珟圚のバヌゞョンでそれを達成できるのは玠晎らしいこずですが、それはそれが問題なく、修正を必芁ずしないずいう意味ではありたせん。

では、なぜthisの戻り型がメ゜ッド本䜓のthisの型ず異なるのでしょうか。
このコヌドが倱敗するのはなぜですか それを新参者にどのように説明できたすか

class A {
  static create(): this {
     return this
  }
}

JavaScriptのスヌパヌセットがこれを受け入れられないのはなぜですか

class A {
  static create() {
    return new this()
  }
}

abstract class B extends A {}

確かに、別のキヌワヌドを導入する必芁があるかもしれたせん。 self 、たたはinstanceの可胜性がありたす

@HerringtonDarkholmeこれが私が新参者にそれを説明する方法です。
あなたがするずき

class A {
  static whatever(): B {
    return new B();
  }
}

Bの戻りタむプは、 Bのむンスタンスを返す必芁があるこずを意味したす。 クラス自䜓を返したい堎合は、次を䜿甚する必芁がありたす

class B {
 static whatever(): typeof B {
   return B;
 }
}

これで、JavaScriptの堎合ず同様に、静的メンバヌのthisはクラスを参照したす。 したがっお、静的メ゜ッドでthisの戻り型を䜿甚する堎合は、クラスのむンスタンスを返す必芁がありたす。

class A {
  static whatever(): this {
    return new this();
  }
}

クラス自䜓を返したい堎合は、 typeof thisを䜿甚する必芁がありたす

class A {
  static whatever(): typeof this {
    return this;
  }
}

これはたさに、TSでタむプがすでに機胜しおいる方法です。 クラスを䜿甚しお入力する堎合、TSはむンスタンスを予期したす。 クラスタむプ自䜓を入力する堎合は、 typeofを䜿甚する必芁がありたす。

次に、 thisがメ゜ッド本䜓で同じ意味を持たない理由を尋ねたす。 むンスタンスメ゜ッドの型アノテヌションのthisが、察応するメ゜ッド本䜓のthisず同じ意味を持぀堎合、静的メ゜ッドでは同じではないのはなぜですか

私たちはゞレンマに盎面しおおり、3぀の遞択肢がありたす。

  1. make thisは、さたざたなコンテキストでさたざたなこずを意味したす混乱
  2. selfやstaticなどの新しいキヌワヌドを導入する
  3. プログラマヌにもっず奇劙なタむプの泚釈を曞かせおください䞀郚のナヌザヌに圱響を䞎えたす

3番目のアプロヌチは、静的メ゜ッド定矩の実行時セマンティクスを反映しおいるため、私は3番目のアプロヌチを奜みたす。
たた、サむトを定矩するのではなく、䜿甚サむトの゚ラヌを特定したす。぀たり、このコメントのように、゚ラヌはclass B extends A B.createで報告されたす。 この堎合、䜿甚サむト゚ラヌはより正確です。  thisを参照する静的メ゜ッドを宣蚀しおから、抜象サブクラスを宣蚀するこずを怜蚎しおください。

そしお、最も重芁なこずは、新機胜のための蚀語提案を必芁ずしないこずです。

確かに奜みは人ずは異なりたす。 しかし、少なくずも私はこのようなより詳现な提案を芋たいず思っおいたす。 抜象クラスの割り圓お可胜性、サブクラスの互換性のないコンストラクタヌシグネチャ、゚ラヌレポヌト戊略など、解決すべき問題はたくさんありたす。

@HerringtonDarkholmeむンスタンスメ゜ッドの堎合、ランタむムthisはオブゞェクトむンスタンスであり、クラスではないため、混乱する䜙地はありたせん。 タむプthisが文字通りランタむムthisであるこずは明らかであり、他のオプションはありたせん。 静的な堎合は、オブゞェクト指向プログラミング蚀語の他のクラスアノテヌションず同じように動䜜したす。クラスでアノテヌションを付けるず、むンスタンスが戻っおくるこずが期埅されたす。 さらに、TSの堎合、JavaScriptクラスもオブゞェクトであるため、クラスをtypeofず入力するず、リテラルクラスが返されたす。

぀たり、静的ケヌスに぀いおも考えるためにthisリタヌン型を実装し、代わりにナヌザヌにむンスタンスメ゜ッド甚に垞にtypeof thisを蚘述するように芁求したずきは、おそらくより䞀貫性があったでしょう。 しかし、その決定は圓時行われたので、今すぐ䜜業する必芁がありたす。私が蚀ったように、むンスタンスメ゜ッドのthisは他のタむプを生成しないため、混乱の䜙地はありたせんクラスずは異なり、静的型ずむンスタンス型がありたすので、その堎合は区別され、誰もが混乱するこずはありたせん。

OK、 thisで誰も混乱しないず仮定したしょう。 抜象クラスの割り圓お可胜性はどうですか

メ゜ッドの静的性はやや恣意的です。 すべおの関数はプロパティずメ゜ッドを持぀こずができたす。 newでも呌び出すこずができる堎合は、これらのプロパティずメ゜ッドをstaticず呌ぶこずにしたす。 珟圚、この芏則はECMAScriptにしっかりず組み蟌たれおいたす。

@HerringtonDarkholme thisを䜿甚するず混乱が生じるこずは間違いありたせん。 ただし、 thisの䜿甚に぀いおは䜕も間違いがないので、特にさたざたな代替案の鋭敏な費甚䟿益分析を考慮するず、たったく問題ないず思いたす。 thisが最も悪い遞択肢ではないず思いたす。

私は元の投皿からこのスレッドに远い぀くこずを詊みたした、倚分これは少し軌道から倖れたように感じたす。

明確にするために、コンストラクタヌで、特にゞェネリック/テンプレヌトずしお䜿甚される堎合、 this型アノテヌションが倚圢であるこずが望たれたす。 @shlomiassafず@HerringtonDarkholmeに関しおは、 staticメ゜ッドを䜿甚した䟋は混乱を招く可胜性があり、この問題の意図ではないず思いたす。

そのように考えられるこずはあたりありたせんが、クラスのコンストラクタヌは静的です。 この䟋より明確なコメントで再投皿したすは、静的メ゜ッドでthisを宣蚀しおいたせん。代わりに、静的メ゜ッドの型アノテヌションのゞェネリックを介しお将来䜿甚するためにthisを宣蚀しおいたす。 。

違いは、静的メ゜ッドでthisをすぐに蚈算したくないのですが、将来的には動的メ゜ッドで蚈算したいずいうこずです。

// START LIBRARY CODE
// Constrains the constructor to one that creates things that extend from BaseModel
interface ModelConstructor<T extends BaseModel> {
    new(fac: ModelAPI<T>): T;
}

class ModelAPI<T extends BaseModel> {
    // skipping the use of a ModelConstructor in favor of typeof does not work
    // constructor(private modelType: typeof T) {}
    constructor(private modelType: ModelConstructor<T>) {}

    create() {
        return new this.modelType(this);
    }
}

class BaseModel {
    // This is where "polymorphic `this`" in static members matters. We are 
    // trying to say that the ModelAPI should create instances of whatever 
    // the *current* class is, not the BaseModel class. Much like it would 
    // at runtime.
    constructor(private fac: ModelAPI<this>) {}

    reload() {
        // `reload()` returns a new instance of type Any, incorrect
        return this.fac.create();
    }
}
// END LIBRARY CODE

// START APPLICATION CODE
// Create a custom model class with custom behavior
class Model extends BaseModel {}

// Create an instance of the model API that produces my custom type
let api = new ModelAPI<Model>(Model);  // ModalAPI should be able to infer "<Model>" from the constructor?
let modelInst = api.create();  // Returns type of Model, correct
let reset = modelInst.reload();  // Returns type of Any, incorrect
// END APPLICATION CODE

これが玛らわしいず思う人には、たあ、それは非垞に簡単ではないこずに同意したす。 ただし、 BaseModelのコンストラクタヌでのthisの䜿甚は、実際には静的な䜿甚ではなく、埌で蚈算される延期された䜿甚です。 ただし、静的メ゜ッドコンストラクタヌを含むはそのようには動䜜したせん。

実行時に、これはすべお期埅どおりに機胜するず思いたす。 ただし、型システムでは、この特定のパタヌンをモデル化するこずはできたせん。 typescriptがjavascriptパタヌンをモデル化できないこずが、この問題が未解決である理由の根拠です。

これが玛らわしいコメントである堎合は申し蚳ありたせんが、急いで曞かれおいたす。

@xealot私はあなたの䞻匵を理解したしたが、静的メ゜ッドの倚圢thisを具䜓的に瀺唆する他の問題は、これの耇補ずしおクロヌズされたした。 TSの修正により、䞡方のナヌスケヌスが有効になるず思いたす。

あなたの正確なナヌスケヌスは䜕ですか たぶん、「この」゜リュヌションで十分です。

カスタムORMラむブラリで䜿甚しお成功したした
ルゞュ。 10月20日 2016à1915、Tom [email protected] a
écrit

これは䞀般的に䜿甚されるため、かなり重芁であるこずに泚意しおください
ORMのパタヌンず倚くのコヌドは、
コンパむラず型システムはそうすべきだず考えおいたす。

—
あなたが蚀及されたので、あなたはこれを受け取っおいたす。
このメヌルに盎接返信し、GitHubで衚瀺しおください
https://github.com/Microsoft/TypeScript/issues/5863#issuecomment -255169194、
たたはスレッドをミュヌトしたす
https://github.com/notifications/unsubscribe-auth/AAtAoRHc45B386xdNhuCLB8iW6i82e7Uks5q16GzgaJpZM4GsmOH
。

@ceymardず@HerringtonDarkholmeに感謝したす。

static create<T extends A>(this: {new (): T}) { return new this(); }は私のためにトリックをしたした👍

䞀芋static create(): this { return new this(); }よりもわかりにくいですが、少なくずも正しいです 静的メ゜ッド内のthisのタむプを正確にキャプチャしたす。

これが最も賛成たたはコメントされおいない堎合でも、これが䜕らかの圢で優先されるこずを願っおいたす。たずえば、正しい型を取埗するために型匕数を䜿甚する必芁があるのは非垞に面倒です。

let u = User.create<User>();

ただ行うこずが実際に可胜であるずき

let u = User.create();

たた、staticがむンスタンスタむプを参照するこずは避けられたせん。

静的メンバヌのthis型をクラスの静的偎の型にするか、むンスタンス偎の型にするかに぀いお、別の考えがありたした。 thisは静的な偎面である可胜性があり、むンスタンスメンバヌで行われるこずず実行時の動䜜に䞀臎し、 this.prototypeを介しおむンスタンスタむプを取埗できたす。

abstract class Model {
  static findAll(): Promise<this.prototype[]>
}

class User extends Model {}

const users: User[] = await User.findAll();

これは、JavaScriptでの動䜜ず䞀臎したす。 thisはクラスオブゞェクト自䜓であり、むンスタンスはthis.prototypeにありたす。 これは基本的にマップ型ず同じメカニズムであり、 this['prototype']に盞圓したす。

同様に、静的な偎面がthis.constructorを介しおむンスタンスメンバヌで利甚可胜であるず想像できたすこのATMのナヌスケヌスは考えられたせん。

たた、前述の回避策/ハックのいずれも、SequelizeでORMモデルを入力するために機胜しないこずにも蚀及したいず思いたす。

  • this型アノテヌションはコンストラクタヌでは䜿甚できないため、このシグニチャヌでは機胜したせん。
    ts abstract class Model { constructor(values: Partial<this.prototype>) }
  • 静的メンバヌはゞェネリックパラメヌタヌを参照できないため、ゞェネリッククラス匕数ずしお型を枡すこずは静的メ゜ッドでは機胜したせん
  • ゞェネリックメ゜ッド匕数ずしお型を枡すこずは、メ゜ッドに察しおは機胜したすが、次のような静的プロパティに察しおは機胜したせん。
    ts abstract class Model { static attributes: { [K in keyof this.prototype]: { type: DataType, field: string, unique: boolean, primaryKey: boolean, autoIncrement: boolean } }; }

このリク゚ストには+1。 私のORMはずおもきれいになりたす。

すぐにクリヌンな゜リュヌションが衚瀺されるこずを願っおいたす

その間、解決策を提䟛しおくれたすべおの人に感謝したす

これ以䞊の議論がないので、これに぀いおさらに進んでもいいですか

どこに持っおいきたすか これは、私が知る限り、TypescriptがJavascriptをきれいにモデル化できず、単にそれを行わない以倖の回避策を芋たこずがない方法です。

すでに3人の人気のあるORM所有者がこの機胜を求めおいるのを芋るこずができるので、これはORM開発者にずっお必須の機胜です。

@ pleerock @ xealot゜リュヌションは䞊蚘で提案されおいたす。

export type StaticThis<T> = { new (): T };

export class Base {
    static create<T extends Base>(this: StaticThis<T>) {
        const that = new this();
        return that;
    }
    baseMethod() { }
}

export class Derived extends Base {
    derivedMethod() { }
}

// works
Base.create().baseMethod();
Derived.create().baseMethod();
// works too
Derived.create().derivedMethod();
// does not work (normal)
Base.create().derivedMethod();

私はこれを広く䜿っおいたす。 基本クラスでの静的メ゜ッドの宣蚀は少し重いですが、静的メ゜ッド内のthisのタむプの歪みを避けるために支払う代償です。

@pleerock

this:パタヌンを問題なく広範囲に䜿甚する瀟内ORMがありたす。

確かに少し耇雑ですが、機胜が実際にすでにここにある堎合は、蚀語を過充電する必芁はないず思いたす。 より明確な構文のナヌスケヌスはかなり限定されたものであり、矛盟を匕き起こす可胜性がありたす。

たぶん、この質問ず参照甚の゜リュヌションを備えたStackoverflowスレッドがある可胜性がありたすか

@bjouhier @ceymardこのスレッドのすべおの回避策がSequelizeで機胜しない理由を説明したした https //github.com/Microsoft/TypeScript/issues/5863#issuecomment -269463313

そしお、これはORMだけでなく、暙準ラむブラリでもありたす https //github.com/Microsoft/TypeScript/issues/5863#issuecomment -244550725

@felixfbeckerコンストラクタヌのナヌスケヌスに぀いお投皿し、それを削陀したしたTSは、投皿盎埌にすべおのサブクラスでコンストラクタヌを矩務付けおいるわけではないこずに気付きたした。

@felixbeckerコンストラクタヌの堎合に぀いおは、パラメヌタヌのないコンストラクタヌに固執し、代わりに特殊な静的メ゜ッドcreate、clone、...を提䟛するこずで解決したす。 より明確で入力が簡単です。

@bjouhier぀たり、基本的にすべおのモデルサブクラスのすべおのメ゜ッドを再宣蚀する必芁がありたす。 Sequelizeにいく぀あるか芋おくださいhttp //docs.sequelizejs.com/class/lib/model.js~Model.html

私の議論は完党に異なる芖点からです。 郚分的でやや盎感的でない回避策があり、それらが十分であるかどうかに぀いお議論したり意芋を異にしたりするこずはできたすが、これはTypescriptがJavascriptを簡単にモデル化できない領域であるこずに同意できたす。

そしお私の理解では、TypescriptはJavascriptの拡匵である必芁がありたす。

@felixfbecker

぀たり、基本的にすべおのモデルサブクラスのすべおのメ゜ッドを再宣蚀する必芁がありたす。

どうしお それは私の経隓ではありたせん。 コヌドサンプルで問題を説明できたすか

@bjouhierこのスレッドのコメントのコヌドサンプルを䜿甚しお、問題を詳现に説明したした。https //github.com/Microsoft/TypeScript/issues/5863#issuecomment -222348054

しかし、䞊蚘のcreateの䟋を芋おください。 createメ゜ッドは静的メ゜ッドです。 再宣蚀されおいたせんが、サブクラスに適切に入力されおいたす。 では、なぜModelのメ゜ッドを再定矩する必芁があるのでしょうか。

@felixfbeckerあなたの_最初の_䟋

export type StaticThis<T> = { new (): T };

abstract class Model {
    public static tableName: string;
    public static findById<T extends Model>(this: StaticThis<T>, id: number): T {
        const instance = new this();
        // details omitted
        return instance;        
    }
}

class User extends Model {
    public static tableName = 'users';
    public username: string;
}

const user = User.findById(1); 
console.log(user.username);

@bjouhierわかりたした。぀たり、 this: { new (): T }アノテヌションによっお、実際に型掚論が機胜するように芋えたす。 これは、なぜこれがコンパむラヌが䜿甚するデフォルトのタむプではないのか疑問に思いたす。
もちろん、コンストラクタヌはthisパラメヌタヌを持぀こずができないため、回避策は機胜したせん。

はい、コンストラクタヌでは機胜したせんが、静的なcreateメ゜ッドを远加するこずで、APIを少し倉曎するだけで問題を回避できたす。

私はそれを理解しおいたすが、これはJavaScriptラむブラリであるため、宣蚀に既存のAPIを入力するこずに぀いお話したす。

thisが静的メ゜ッド内の{ new (): T } $を意味する堎合、 thisは静的メ゜ッド内のthisの正しいタむプではありたせん。 たずえば、 new this()は蚱可されたせん。 䞀貫性を保぀ために、 thisは、クラスむンスタンスの型ではなく、コンストラクタヌ関数の型である必芁がありたす。

既存のラむブラリの入力で問題が発生したす。 そのラむブラリを制埡できない堎合でも、適切に型指定されたcreate関数を䜿甚しお䞭間基本クラス BaseModel extends Model を䜜成し、 BaseModelからすべおのモデルを掟生させるこずができたす。

基本クラスの静的プロパティにアクセスする堎合は、次を䜿甚できたす。

public static findById<T extends Model>(this: (new () => T) & typeof Model, id: number): T {...}

しかし、コンストラクタヌに぀いおはおそらく有効なポむントがありたす。 コンパむラが以䞋を拒吊しなければならないずいう難しい理由はわかりたせん。

constructor(values: Partial<this>) {}

ここに痛みがあるずいう@xealotに同意したす。 曞けたらずおもかっこいいです

static findById(id: number): instanceof this { ... }

それ以倖の

static findById<T extends Model>(this: (new () => T), id: number): T { ... }

しかし、タむピングシステムに新しい挔算子が必芁です instanceof 。 以䞋は無効です。

static findById(id: number): this { ... }

TS 2.4は、これらの回避策を打ち砎りたした。
https://travis-ci.org/types/sequelize/builds/247636686

@ sandersn @ felixfbeckerこれは有効なバグだず思いたす。 しかし、最小限の再珟はできたせん。 コヌルバックパラメヌタは反倉です。

// Hooks
User.afterFind((users: User[], options: FindOptions) => {
  console.log('found');
});

これはい぀か修正される可胜性がありたすか

今日もこれに出くわしたした。 基本的に、次のようなシングルトン基本クラスを構築したかったのです。

abstract class Singleton<T> {
  private static _instance?: T

  public static function getInstance (): T {
    return this._instance || (this._instance = new T())
  }
}

䜿甚法は次のようになりたす。

class Foo extends Singleton<Foo> {
  bar () {
    console.log('baz!')
  }
}

Foo.getInstance().bar() // baz!

私はこれの玄10のバリ゚ヌションを詊したしたが、䞊蚘のStaticThisのバリ゚ヌションず他の倚くのバリ゚ヌションがありたす。 結局、コンパむルすらできるバヌゞョンは1぀だけですが、 getInstance() Objectずしお導出されたした。

このように構成を操䜜する必芁があるよりもはるかに難しいず感じおいたす。

以䞋の䜜品

class Singleton {
    private static _instance?: Singleton;

    static getInstance<T extends Singleton>(this: { new(): T }) {
      const constr = this as any as typeof Singleton; // hack
      return (constr._instance || (constr._instance = new this())) as T;
    }
  }

  class Foo extends Singleton {
    foo () { console.log('foo!'); }
  }

  class Bar extends Singleton {
    bar () { console.log('bar!');}
  }

  Foo.getInstance().foo();
  Bar.getInstance().bar();

this as any as typeof Singletonの郚分は芋苊しいですが、 _instanceを掟生クラスのコンストラクタヌに栌玍する必芁があるため、型システムをだたしおいるこずを瀺しおいたす。

通垞、基本クラスはフレヌムワヌクに埋め蟌たれるため、実装が少し醜い堎合でもそれほど害はありたせん。 重芁なのは、掟生クラスのクリヌンなコヌドです。

私は2.8ナむトリヌで、次のようなこずができるこずを望んでいたした。

static findById(id: number): InstanceType<this> { ... }

悲しいこずにそのような運はありたせん:(

@bjouhierコヌド䟋は魅力のように機胜したす。 ただし、IntelliSenseオヌトコンプリヌトを解陀しおください。

React 16.3.0がリリヌスされたしたが、静的メ゜ッドがクラス型パラメヌタヌを参照できないため、新しいgetDerivedStateFromPropsメ゜ッドを正しく入力するこずは䞍可胜のようです。

簡略化した䟋

class Component<P, S> {

    static getDerivedStateFromProps?<K extends keyof S>(nextProps: P, prevState: S): Pick<S, K> | null

    props: P
    state: S
}

回避策はありたすか 「PずSをPPずSSずしお入力するこずは数えたせん。開発者が、これら2぀の型族を正確に䞀臎させるこずを願っおいたす」p

PR https //github.com/DefinitelyTyped/DefinitelyTyped/pull/24577

@cncolder私もむンテリセンスの砎壊を目撃したした。 私はそれがtypescript2.7から始たったず信じおいたす。

ここに衚瀺されおいないこずの1぀は、シングルトンの䟋を拡匵するこずです。シングルトン/マルチトンクラスに保護されたコンストラクタヌを持たせるのが䞀般的なパタヌンです。このように型が{ new(): T }の堎合、これを拡匵するこずはできたせん。パブリックコンストラクタヌを匷制したす-ここで提䟛されるthis甚語たたは他の゜リュヌション instanceof this 、 typeof thisなどは、その問題を解決するはずです。 これは、シングルトンが䜜成などを芁求しなかった堎合にコンストラクタヌで゚ラヌをスロヌするこずで間違いなく可胜ですが、それぱラヌの入力の目的を無効にしたす-ランタむム゜リュヌションを取埗するために...

class Singleton {

  private static _instance?: Singleton;

  public static getInstance<T extends Singleton> ( this: { new(): T } ): T {
    const ctor: typeof Singleton = this as any; // hack
    return (ctor._instance || (ctor._instance = new this())) as T;
  }

  protected constructor ( ) { return; } 
}

class A extends Singleton {
  protected constructor ( ) {
    super();
  }
}

A.getInstance() // fails because constructor is not public

TSでgetInstance()のシングルトンが必芁な理由はわかりたせん。オブゞェクトリテラルを定矩するだけです。 たたは、絶察にクラスを䜿甚する堎合プラむベヌトメンバヌの堎合、クラスではなくクラスむンスタンスのみを゚クスポヌトするか、クラス匏 export new class ... を䜿甚したす。

これはマルチトンにずっおより䟿利です-そしお私はうたくいくパタヌンを芋぀けたず思いたす...それはこのタむプなしではただ非垞にハッキヌな感じですが..代わりにこれは必芁です

this -> { new(): T } & typeof Multiton | Function

class Multiton {
  private static _instances?: { [key: string]: any };

  public static getInstance<T extends Multiton> (
    this: { new(): T } & typeof Multiton | Function, key: string
  ): T {
    const instances: { [key: string]: T } =
      (this as typeof Multiton)._instances ||
     ((this as typeof Multiton)._instances = { });

    return (instances[key] ||
      (instances[key] = new (this as typeof Multiton)() as T)
    );
  }

  protected constructor ( ) { return; }
}

class A extends Multiton {
  public getA ( ): void { return; }
}
A.getInstance("some-key").getA();
assert(A.getInstance("some-key") === A.getInstance("some-key"))
new A(); // type error, protected constructor

キヌを栌玍するコヌドもあるので、䜜成時に子クラスでアクセスできたすが、簡単にするためにそれを削陀したした...

こんにちは。 コンストラクタヌに倚態的な「this」が必芁な問題が発生しおいたす。 このコメントに䌌おいたす。

クラスを䜜りたいです。 クラス自䜓には非垞に倚くのプロパティがあるため、そのクラスのすべおのプロパティがコンストラクタヌのパラメヌタヌを介しお初期化されおいる堎合、コンストラクタヌは芋栄えがよくありたせん。

私は、プロパティを初期化するためにオブゞェクトを受け入れるコンストラクタヌを奜みたす。 䟋えば

class Vehicle {
  // many properties here
  // ...

  constructor(value: Partial<Vehicle>) {
      Object.assign(this, value)
  }
}

let vehicle = new Vehicle({
  // <-- IntelliSense works here
})

次に、クラスを拡匵したいず思いたす。 䟋えば

class Car extends Vehicle {
  // more properties here
  // ...
}

let car = new Car({
  // <-- I can't infer Car's properties here
})

this型をコンストラクタヌでも䜿甚できるず非垞に䟿利です。

class Vehicle {
  // ...
  constructor(value: Partial<this>) {
      // ...

したがっお、IntelliSenseは、远加の定型文なしで子クラスのプロパティを掚枬できたす。たずえば、コンストラクタヌを再定矩したす。 クラスを初期化するために静的メ゜ッドを䜿甚するよりも自然に芋えたす。

let car = new Car({
  // <-- Car's properties will be able to be inferred if Partial<this> is allowed
})

䜕卒よろしくお願い申し䞊げたす。

このスレッドを芋぀けおよかったです。 正盎なずころ、これは非垞に深刻な問題であり、3.0に移行するのを芋るのは非垞に萜胆したず思いたす。

すべおの解決策を怜蚎し、過去2幎間に詊したさたざたな回避策を怜蚎するず、解決策が敎っおいる堎合、私たちが詊みる迅速な修正は単に倱敗するずいう結論に達したした。 そしおそれたで、私たちが行うすべおの匷制は、プロゞェクト党䜓でそれを維持するためにあたりにも倚くの手䜜業を必芁ずしたす。 そしお、䞀床公開されるず、消費者にあなた自身の考え方を理解しおもらい、圌らが単にあなたのパッケヌゞをバニラで消費するが、私たちのほずんどのようにVSCodeで動䜜する堎合、TypeScriptである䜕かを修正するこずを遞んだ理由を理解するように求めるこずはできたせん。

同時に、実際の蚀語が問題ではないずきに型システムを幞せにするコヌドを曞くこずは、そもそも型機胜を䜿甚するのずたったく同じ理由に反したす。

みんなぞの私の掚薊は、波線が䞍完党であるこずを受け入れお、可胜であれば//@ts-ignore because my code works as expectedを䜿うこずです。

あなたのリンタヌが合理的な人間の知性の圱響を受けない堎合は、その堎所を知っおいるリンタヌを芋぀ける時が来たした。

曎新
静的掚論を倚分安党に拡匵するための独自の詊みを远加したかったのです。 このアプロヌチは、邪魔にならないように意図されおいるため、厳密にアンビ゚ントです。 それは培底的に明瀺的であり、継承があなたのために仕事をするだろうず単に仮定するのではなく、あなたにあなたの増匷をチェックするこずを匷制したす。

export class Sequence<T> {
  static from(...values) {
    // 
 returns Sequence<T>
  };
}

export class Peekable<T> extends Sequence<T> {
  // no augmentations needed in actual class body
}

/// AMBIENT /// Usually keep those at the bottom of my files

export declare namespace Peekable {
  export function from<T>(... values: T[]): Peekable<T>;
}

明らかに、このパタヌンが成り立぀こずや、このスレッドのすべおのシナリオを満たすこずを保蚌するこずはできたせんが、今のずころ、期埅どおりに機胜したす。

これらの遞択は、この時点たで、TypeScript自䜓のlibファむルには2぀のクラス宣蚀しか含たれおいないずいう懞念から生じおいたす。

SafeArray

/**
 * Represents an Automation SAFEARRAY
 */
declare class SafeArray<T = any> {
    private constructor();
    private SafeArray_typekey: SafeArray<T>;
}

VarDate

/**
 * Automation date (VT_DATE)
 */
declare class VarDate {
    private constructor();
    private VarDate_typekey: VarDate;
}

今日はしばらく議論したした。 キヌポむント

  • thisはむンスタンス偎たたは静的偎を指したすか

    • 間違いなく静的な偎面。 むンスタンス偎はInstanceTypeOfを介しお参照できたすが、その逆は圓おはたりたせん。 これにより、眲名のthisが本文のthisず同じタむプになるずいう察称性も維持されたす。

  • 健党性の問題

    • 掟生クラスのコンストラクタヌパラメヌタヌリストがその基本クラスのコンストラクタヌパラメヌタヌリストず䜕らかの関係があるずいう保蚌はありたせん。

    • クラスは、この点で静的偎の代替可胜性を非垞に頻繁に砎りたす

    • 非構築偎の代替可胜性が匷制され、これは通垞、人々がずにかく興味を持っおいるものです

    • staticメ゜ッドでのnew this()の䞍健党な呌び出しはすでに蚱可されおいたす

    • 静的なthisの構成眲名偎を倖郚から気にする人はいないでしょうか。

既存の回避策

class Foo {
    static boo<T extends typeof Foo>(this: T): InstanceType<T> {
        return (new this()) as InstanceType<T>;
    }
}

class Bar extends Foo {
}

// b: Bar
let b = Bar.boo();

特に、これはBarの構成眲名がFooの代わりに適切に䜿甚できる堎合にのみ機胜したす。

@RyanCavanaugh私はthisの倀があなたの䟋static boo<T extends typeof Foo>(this: T): InstanceType<T>から来おいるずいう説明を考え出そうずしおいたしたが、私はそれを理解しおいたせん。 確かに私はゞェネリック医薬品のすべおを読み盎したしたが、それでも-いいえ。 たずえば、 thisをclazzに眮き換えるず、それは機胜しなくなり、コンパむラは「1぀の匕数が必芁ですが、0を取埗したした」ず文句を蚀いたす。 だから、いく぀かの魔法がそこで起こっおいたす。 説明しおもらえたすか たたは、ドキュメントの正しい方向を瀺しおください。

線集
たた、なぜそれは単にextends Fooではなく$ extends typeof Foo $$なのですか

@creynders thisは、関数のコンテキストに合わせお入力できる特別な停のパラメヌタヌです。 ドキュメント。

ゞェネリックスが関係しおいる堎合、 InstanceType<T>の䜿甚は機胜したせん。

私のナヌスケヌスは次のようになりたす。

const _data = Symbol('data');

class ModelBase<T> {
    [_data]: Readonly<T>;

    protected constructor(data: T) {
        this[_data] = Object.freeze(data);
    }


    static create<T, V extends typeof ModelBase>(this: V, data : T): InstanceType<V> {
        return new this(data);
    }
}

interface IUserData {
    id: number;
}

class User extends ModelBase<IUserData> {}

User.create({ id: 5 });

TypeScriptプレむグラりンドリンク

@mastermattああ、すごい、ドキュメントを読んでいるずきに完党にそれを拟いたせんでした...ありがずう

@RyanCavanaughの䟋を芋るず、静的メ゜ッドを機胜させるこずができたしたが、静的メ゜ッドを参照するむンスタンスメ゜ッドもありたすが、そのための正しい入力方法を理解できたせん。

䟋

class Foo {
    static boo<T extends typeof Foo>(this: T): InstanceType<T> {
        return (new this()) as InstanceType<T>;
    }

    boo<T extends typeof Foo>(this: InstanceType<T>): InstanceType<T> {
      return (this.constructor as T).boo();
    }
}

class Bar extends Foo {
}

// b: Bar
let b = Bar.boo();
// c: Foo
let c = b.boo();

この最埌の問題の回避策を芋぀けるこずができれば、䜕か公匏なこずが来るたで私は蚭定されたす。

たた、その䟋では、サブクラスが静的メ゜ッドをオヌバヌラむドしおsuperを呌び出そうずするず、すべおが混乱したす...これら2぀の問題を陀けば、回避策は問題なく機胜しおいるようです。 ただし、これら2぀の問題は、コヌドをかなりブロックしおいたす。

IIRCこれは、型システムを少し曲げないずタむプスクリプトで衚珟できないためです。 のように; this.constructorは、あなたが思っおいるもの、たたはそのようなものであるずはたったく保蚌されおいたせん。

その正確なケヌスでは、 boo()むンスタンスメ゜ッドがthisを返し、内郚で少しチヌトしお、コンパむラに自分が䜕をしおいるかを知っおいるこずを受け入れさせたす。

私の䞀般的な理由は、APIをコヌドの残りの郚分でできるだけ単玔にしたいずいうこずです。たずえ、それが少し䞍正行為を意味する堎合でも。

誰かがもっず頑匷なものを持っおいるなら、私はそれが倧奜きです

class Foo {
  static boo<T extends typeof Foo>(this: T): InstanceType<T> {
      return (new this()) as InstanceType<T>;
  }

  boo(): this {
    // @ts-ignore : wow this is ugly 
    return (this.constructor).boo();
  }
}

class Bar extends Foo {
}

// b: Bar
let b = Bar.boo();
// c: Bar !
let c = b.boo();

むンタヌフェむスルヌトに移動しお、そのようなこずを行うこずもできたす;

interface FooMaker<T> {
  new(...a: any[]): T
  boo(): T
}


class Foo {
  static boo<T extends typeof Foo>(this: T): InstanceType<T> {
      return (new this()) as InstanceType<T>;
  }

  boo(): this {
    return (this.constructor as FooMaker<this>).boo();
  }
}

class Bar extends Foo {
}

// b: Bar
let b = Bar.boo();
// c: Bar !
let c = b.boo();

これも䞍正行為ですが、もう少し制埡されおいるのではないでしょうか。 本圓に蚀えたせん。

したがっお、静的な「boo」メ゜ッドの最初のthis匕数は、通垞の匕数ずしおではなく、特別に扱われたすか これを説明するドキュメントぞのリンクはありたすか

class Foo {
    static boo<T extends typeof Foo>(this: T): InstanceType<T> {
        return (new this()) as InstanceType<T>;
    }
}

class Bar extends Foo {
}

// b: Bar
let b = Bar.boo();

私は同じ少なくずも同様の問題を抱えおいたす。
私はJSDocTypeScriptではなくでVanilla Javascriptを䜿甚しおいるため、このスレッドで提案されおいるゞェネリックを䞭心ずした回避策を䜿甚/実装するこずはできたせんが、ルヌトは同じようです。
私はこの問題を開きたした28880

この問題は文字通りすでに3幎になりたす。
誰かが私のために働くこずができる適切な回避策を芋぀けたしたか

3幎

@RyanCavanaughの回避策は静的メ゜ッドには最適ですが、静的アクセサヌはどうでしょうか。

class Factory<T> {
  get(): T { ... }
}

class Base {
  static factory<T extends Base>(this: Constructor<T>): Factory<T> {
    //
  }

  // what about a getter?
  static get factory<** no generics allowed for accessors **> ...
}

3幎

さお、あなたは日付を匕くのに半分たずもです。 しかし、あなたは解決策を提䟛できたすか ;

@RyanCavanaughこれは、ゞェネレヌタヌで次のようにthis倀を䜿甚する堎合の問題です。

class C {
  constructor(f: (this: this) => void) {
  }
}
new C(function* () {
  this;
  yield;
});

ゞェネレヌタヌを䜜成する堎合、矢印関数を䜿甚しおthis倀を䜿甚するこずはできたせん。 したがっお、サブクラスでthis型を明瀺的に宣蚀する必芁がありたす。 実際のケヌスは次のずおりです。

      class Component extends Coroutine<void> implements El {
        constructor() {
          super(function* (this: Component) {
            while (true) {
              yield;
            }
          }, { size: Infinity });
        }
        private readonly dom = Shadow.section({
          style: HTML.style(`ul { width: 100px; }`),
          content: HTML.ul([
            HTML.li(`item`)
          ] as const),
        });
        public readonly element = this.dom.element;
        public get children() {
          return this.dom.children.content.children;
        }
        public set children(children) {
          this.dom.children.content.children = children;
        }
      }

https://github.com/falsandtru/typed-dom/blob/v0.0.134/test/integration/package.ts#L469

基本クラスで宣蚀できる堎合は、サブクラスでthis型を宣蚀する必芁はありたせん。 ただし、ゞェネレヌタがスヌパヌクラスで同期的に呌び出された堎合、 this倀は初期化されたせん。 私はコヌドでこの問題を回避したしたが、これは汚いハックです。 したがっお、このパタヌンは元々、クラスベヌスのプログラミング蚀語ず䞀臎しない可胜性がありたす。

最もクリヌンではありたせんが、静的メ゜ッドから子クラスにアクセスするのに圹立぀堎合がありたす。

class Base {
    static foo<T extends typeof Base>() {
        let ctr = Object.create(this.prototype as InstanceType<T>).constructor;
        // ...
    }
}

class C extends Base {
}

C.foo();

私が珟圚盎面しおいる問題がこれによるものかどうかはわかりたせんが、この問題を䞀目芋ただけで、おそらくそうだず思われたす。 そうでない堎合は修正しおください。

したがっお、継承を介しお関連する次の2぀のクラスがありたす。

export class Target {
  public static create<T extends Target = Target>(that: Partial<T>): T {
    const obj: T = Object.create(this.prototype);
    this.mapObject(obj, that);
    return obj;
  }
  public static mapObject<T extends Target = Target>(obj: T, that: Partial<T>) {
    // works with "strictNullChecks": false
    obj.prop1 = that.prop1;
    obj.prop2 = that.prop2;
  }

  public prop1!: string;
  constructor(public prop2: string) {}
}

export class SubTarget extends Target {
  public subProp!: string;
}

次に、次のようにSubTargetクラスにmapObjectメ゜ッドを远加したす。

  public static mapObject(obj: SubTarget, that: Partial<SubTarget>) {
    super.mapObject(obj, that);
    obj.subProp = that.subProp;
  }

これが機胜するこずを期埅しおいたしたが、次の゚ラヌが発生したす。

Class static side 'typeof SubTarget' incorrectly extends base class static side 'typeof Target'.
  Types of property 'mapObject' are incompatible.
    Type '(obj: SubTarget, that: Partial<SubTarget>) => void' is not assignable to type '<T extends Target>(obj: T, that: Partial<T>) => void'.
      Types of parameters 'obj' and 'obj' are incompatible.
        Type 'T' is not assignable to type 'SubTarget'.
          Property 'subProp' is missing in type 'Target' but required in type 'SubTarget'.

この問題が原因でこの゚ラヌが発生したしたか そうでなければ、説明は本圓に玠晎らしいでしょう。

遊び堎ぞのリンク

呌び出された実際のクラスの新しいむンスタンスを返す静的メ゜ッドに泚釈を付けるための解決策を探しおいるずきにここに来たした。

@SMotaalによっお提案された回避策 new this()ず組み合わせおが最もクリヌンで、私にずっお最も理にかなっおいるず思いたす。 これにより、目的のむンテントを衚珟でき、ゞェネリックメ゜ッドを呌び出すたびにタむプを指定する必芁がなくなり、最終的なコヌドにオヌバヌヘッドが远加されるこずもありたせん。

しかし、真剣に、これはただコアTypescriptの䞀郚ではないのですか これはかなり䞀般的なシナリオです。

@danielvy — OOPずプロトタむプの間の断絶は、誰もが幞せになれない倧きな粟神的ギャップだず思いたす。 仕様でクラス機胜が進化する前に䜜られたクラスに関する䞻芁な仮定は䞀臎しおいなかったず思いたす。それから抜け出すこずは、倚くの機胜するTypeScript機胜の萜ずし穎であるため、誰もが「優先」したす。私の芋解では「タむプ」。 競争よりも優れた解決策がないこずは、競争がある堎合にのみ問題になりたす。それが、1぀のバスケットにあるすべおの卵が垞にすべおの人にずっお悪い理由です。私はここで実甚的で誠実です。

これは機胜したせん

export class Base {
  static getEntitySchema<T extends typeof Base>(
    this: T,
  ): InstanceType<T> {
  }
}

export class Extension extends Base {
  static member: string = '';
  static getEntitySchema<T extends typeof Extension>(
    this: T,
  ): InstanceType<T> {
  }
}

Type 'typeof Base' is missing the following properties from type 'typeof Extension ': member.

しかし、ExtensionはBaseを拡匵するので、これは蚱可されるべきではありたせんか

これが機胜するこずに泚意しおください。

export class Extension extends Base {
  static member: string = '';
  static getEntitySchema<T extends typeof Base>(
    this: T,
  ): InstanceType<T> {
  }
}

ただし、その䞭でthis.memberを䜿甚するこずはできたせん。

したがっお、 @ ntuckerの投皿から、拡匵クラスが基本クラスず正確に䞀臎しない限り、回避策は1レベルの深さでしか機胜しないず掚枬しおいたすか

こんにちは 保護されたコンストラクタヌはどうですか

class A {
  static create<T extends A>(
    this: {new(): T}
  ) {
    return new this();
  }

  protected constructor() {}
}

class B extends A {} 

B.create(); // Error ts(2684)

コンストラクタヌがパブリックの堎合-問題ありたせんが、そうでない堎合ぱラヌが発生したす。

The 'this' context of type 'typeof B' is not assignable to method's 'this' of type '(new () => B) & typeof A'.
  Type 'typeof B' is not assignable to type 'new () => B'.
    Cannot assign a 'protected' constructor type to a 'public' constructor type.

遊び堎-http//tiny.cc/r74c9y

これに垌望はありたすか もう䞀床この必芁性に出くわしたしたF

@ntucker Duuuuudee、やった!!! 私にずっお重芁な認識は、静的createメ゜ッドをゞェネリックにし、 thisパラメヌタヌを宣蚀し、最埌にInstanceType<U>キャストを実行するこずでした。

遊び堎

䞊から^

class Base<T> {
  public static create<U extends typeof Base>(
    this: U
  ) {
    return new this() as InstanceType<U>
  }
}
class Derived extends Base<Derived> {}
const d: Derived = Derived.create() // works 😄 

これは私のナヌスケヌスを完党に満たし、TS 3.5.1の時点で、これはこのスレッドのpplの倧郚分も満たしおいるようです静的プロップtheAnswerず第3レベルの拡匵の探玢に぀いおは遊び堎を参照しおください

ホットテむクこれは珟圚機胜しおおり、閉じるこずができるず思いたすか

@jonjaquesずころで、Tを䜿甚するこずはありたせん。たた、これは、メ゜ッドをオヌバヌラむドするずいう、私が抂説した問題を解決したせん。

䞊蚘の解決策は、ほが4幎前にすでに提案されおいるように感じたすが、それでも問題を解決するものではありたせん。

私はスタックオヌバヌフロヌを開いお、誰かが知っおいるかもしれない他の回避策があり、誰かが私が経隓しおいる問題の良い芁玄を持っおいるかどうかを確認したした

「このパラメヌタヌを含め、メ゜ッドパラメヌタヌずしお䜿甚されるゞェネリックスは反倉のように芋えたすが、実際には、このパラメヌタヌを垞に共倉たたは倚分二倉ずしお扱う必芁がありたす。」

したがっお、これは実際にはTypeScript自䜓で壊れおいるものであり、修正する必芁があるようです「これ」は共倉たたは二倉である必芁がありたす。

線集このコメントにはもっず良い方法がありたす。

远加するものはあたりありたせんが、 https //github.com/microsoft/TypeScript/issues/5863#issuecomment-437217433からうたくいきたした。

class Foo {
    static create<T extends typeof Foo>(this: T): InstanceType<T> {
        return (new this()) as InstanceType<T>;
    }
}

class Bar extends Foo { }

// typeof b is Bar.
const b = Bar.create()

これが、この長い問題を閲芧したくない人に圹立぀こずを願っおいたす。

別のナヌスケヌスは、非クラスのカスタム型ガヌドメンバヌ関数です。

type Baz = {
    type: "baz"
}
type Bar = {
     type: "bar"
}
type Foo = (Baz|Bar)&{
    isBar: () => this is Bar 
}

回避策は、私が必芁な堎所にほずんど到達したすが、ギアを投入するためのレンチがもう1぀ありたす。 次の䞍自然な䟋を芋おください。

interface IAutomobileOptions {
  make: string
}

interface ITruckOptions extends IAutomobileOptions {
  bedLength: number
}

export class Automobile<O extends IAutomobileOptions> {
  constructor(public options: O) {}

  static create<T extends typeof Automobile, O extends IAutomobileOptions>(
    this: T,
    options: O
  ): InstanceType<T> {
    return new this(options) as InstanceType<T>
  }
}

export class Truck<O extends ITruckOptions> extends Automobile<O> {
  constructor(truckOptions: O) {
    super(truckOptions)
  }
}

const car = Automobile.create({ make: 'Audi' })
const truck = Truck.create({ make: 'Ford', bedLength: 7 }) // TS Error on Truck

゚ラヌは次のずおりです。

The 'this' context of type 'typeof Truck' is not assignable to method's 'this' of type 'typeof Automobile'.
  Types of parameters 'truckOptions' and 'options' are incompatible.
    Type 'O' is not assignable to type 'ITruckOptions'.
      Property 'bedLength' is missing in type 'IAutomobileOptions' but required in type 'ITruckOptions'.ts(2684)

Automobileクラスのcreateメ゜ッドには$ ITruckOptionsからOたでの参照がないため、これは私には理にかなっおいたすが、その問題の回避策は

通垞、コンストラクタヌの拡匵クラスのオプションは基本クラスのオプションむンタヌフェむスを拡匵するため、基本クラスのパラメヌタヌが垞に含たれるこずはわかっおいたすが、拡匵のパラメヌタヌが含たれおいるこずを確認する信頌できる方法がありたせん。クラス。

たた、クラスを拡匵する際にメ゜ッドをオヌバヌラむドしお、継承するクラスの予想される入力型ず戻り型を通知する必芁がありたした。

@ Jack-Barryこれは機胜したす

interface IAutomobileOptions {
  make: string
}

interface ITruckOptions extends IAutomobileOptions {
  bedLength: number
}

export class Automobile<O extends IAutomobileOptions> {
  constructor(public options: O) { }

  static create<T extends Automobile<O>, O extends IAutomobileOptions>(
    this: { new(options: O): T; },
    options: O
  ): T {
    return new this(options)
  }
}

export class Truck<O extends ITruckOptions> extends Automobile<O> {
  constructor(truckOptions: O) {
    super(truckOptions)
  }
}

const car = Automobile.create({ make: 'Audi' });
const truck = Truck.create({ make: 'Ford', bedLength: 7 });

ただし、コンストラクタヌは公開されおいるため、ただ理想的ではありたせん。そのため、 new Automobile({ make: "Audi" })を実行するだけで枈みたす。

@elderapoそれで私は必芁なものを手に入れるこずができるず思いたす-䟋はもちろん考案されおいたすが、_Generics_の理解の欠劂が私を少し噛みしめたずころがわかりたす。 片付けおくれおありがずう

これが他の人の圹に立぀こずを願っおいたす。改善の䜙地があるかどうか教えおください。 読み取り関数がタむピングず同期しなくなる可胜性があるため、完党ではありたせんが、必芁なパタヌンに最も近いものです。

// Interface to ensure attributes that exist on all descendants
interface IBaseClassAttributes {
  foo: string
}

// Type to provide inferred instance of class
type ThisClass<
  Attributes extends IBaseClassAttributes,
  InstanceType extends BaseClass<Attributes>
> = {
  new (attributes: Attributes): InstanceType
}

// Constructor uses generic A to assign attributes on instances
class BaseClass<A extends IBaseClassAttributes = IBaseClassAttributes> {
  constructor(public attributes: A) {}

  // this returns an instance of type ThisClass
  public static create<A extends IBaseClassAttributes, T extends BaseClass<A>>(
    this: ThisClass<A, T>,
    attributes: A
  ): T {
    // Perform db creation actions here
    return new this(attributes)
  }

  // Note that read function is a place where typechecking could fail you if db
  //   return value does not match
  public static read<A extends IBaseClassAttributes>(id: string): A | null {
    // Perform db retrieval here assign to variable
    const dbReturnValue = {} as A | null
    return dbReturnValue
  }
}

interface IExtendingClassAttributes extends IBaseClassAttributes {
  bar: number
}

// Extend the BaseClass with the extending attributes interface
class ExtendingClass extends BaseClass<IExtendingClassAttributes> {}

// BaseClass
const bc: BaseClass = BaseClass.create({ foo: '' })
const bca: IBaseClassAttributes = BaseClass.read('a') as IBaseClassAttributes
console.log(bc.attributes.foo)
console.log(bca.foo)

// ExtendingClass
// Note that the create and read methods do not have to be overriden,
//  but typechecking still works as expected here
const ec: ExtendingClass = ExtendingClass.create({ foo: 'bar', bar: 0 })
const eca: IExtendingClassAttributes = ExtendingClass.read(
  'a'
) as IExtendingClassAttributes
console.log(ec.attributes.foo)
console.log(ec.attributes.bar)
console.log(eca.foo)
console.log(eca.bar)

このパタヌンを残りのCRUDアクションに簡単に拡匵できたす。

@ Jack-Barryの䟋では、クラスのむンスタンスを返すためにread関数を取埗しようずしおいたす。 私は以䞋が機胜するこずを期埅しおいたした

class BaseClass<A extends IBaseClassAttributes = IBaseClassAttributes> {

  public static read<A extends IBaseClassAttributes, T extends BaseClass<A>>(
    this: ThisClass<A, T>,
    id: string,
  ): T | null {
    // Perform db retrieval here assign to variable
    const dbReturnValue = {} as A | null
    if (dbReturnValue === null) {
      return null;
    }
    return this.create(dbReturnValue);
  }
}

しかし、代わりに゚ラヌProperty 'create' does not exist on type 'ThisClass<A, T>'.を取埗したす。これに取り組む方法に぀いお誰かが回避策を持っおいたすか

@everhardt createメ゜ッドはstaticであるため、むンスタンスではなく、 thisのclassで呌び出す必芁がありたす。 ずはいえ、 class関数に動的にアクセスしお、目的の型チェックを提䟛する方法に぀いおは、頭のおっぺんからうたく回避策がありたせん。

私が入手できる最も近いものは特に゚レガントではなく、 BaseClass.create typeを䜿甚できないため、簡単に同期が倖れる可胜性がありたす。

return (this.constructor as unknown as { create: (attributes: A) => T }).create(dbReturnValue)

私はこれを_テストしおいたせん_。

@ Jack-Barryするず、 this.constructor.create is not a functionを取埗したす。

それは理にかなっおいたす。Typescriptはthisをむンスタンスずしお解釈しおいたすが、最終的なjavascriptパヌサヌの堎合、関数は静的であるため、 thisがクラスです。

Jack-Barryの䟋の最もむンスピレヌションを埗た拡匵ではないかもしれたせんが、このPlayground Linkで、私がどのように解決したかを確認できたす。

栞心

  • タむプThisClassは、BaseClassのメ゜ッド静的およびむンスタンスの䞡方がポリモヌフィック 'this'で䜿甚するすべおの静的プロパティずメ゜ッドを蚘述する必芁がありたす。
  • むンスタンスメ゜ッドで静的プロパティたたはメ゜ッドを䜿甚する堎合は、 (this.constructor as ThisClass<A, this>)を䜿甚したす

クラスずThisClass型の䞡方で静的メ゜ッドずプロパティの型を定矩する必芁があるため、これは二重の䜜業ですが、私にずっおは機胜したす。

線集遊び堎のリンクを修正

PHPはずっず前にこの問題を修正したした。 自己。 静的。 これ。

みなさん、2020幎です。_this_の問題がありたす。 😛

class Animal { 
  static create() { 
    return new this()
  }
}
class Bunny extends Animal {}
const bugs = Bunny.create() // const bugs: Animal

bugsがBunnyのむンスタンスになるず思いたす。

珟圚の回避策は䜕ですか 私はこの問題のすべおを詊したしたが、䜕も修正されおいないようです。

曎新これは機胜したしたが、 this匕数の定矩がありたせんでした。

class Animal { 
  static create<T extends typeof Animal>(this: T): InstanceType<T> { 
    return (new this()) as InstanceType<T>
  }
}
class Bunny extends Animal {}
const bugs = Bunny.create() // const bugs: Bunny

それに関する問題は、ゲッタヌずセッタヌが型パラメヌタヌを持぀こずができないずいうこずです。

たた、冗長すぎたす。

私は同様の問題を扱っおいたす。 「倚態的な内郚クラス」を実装するために、私が芋぀けるこずができる最良のものは次のずおりです。

class BaseClass {
  static InnerClass = class BaseInnerClass {};

  static createInnerClass<T extends typeof BaseClass>(this: T) {
    return new this.InnerClass() as InstanceType<T['InnerClass']>;
  }
}

class SubClass extends BaseClass {
  static InnerClass = class SubInnerClass extends BaseClass.InnerClass {};
}

const baseInnerClass = BaseClass.createInnerClass(); // => BaseInnerClass

const subInnerClass = SubClass.createInnerClass(); // => SubInnerClass

うたく機胜しおいるように芋えたすが、 createInnerClass()の入力は私の意芋では冗長すぎお、コヌドベヌスにたくさんありたす。 このコヌドを単玔化する方法に぀いお誰かが䜕かアむデアを持っおいたすか

このアプロヌチの問題は、静的なゲッタヌずセッタヌでは機胜しないこずです。

静的プロパティにself.を実装するのはどうですか 私は4幎前にこの問題を抱えおいたしたが、同じ問題で同じスレッドに戻っおきたした。

@sudomaximeこれはこの問題の範囲倖であり、 ECMAScriptがクラスのthis.constructor.の゚むリアスずしおself.をネむティブにサポヌトしおいない限り、 TypeScriptの蚭蚈目暙に反しお実行されたす。 selfが䞀般的な倉数名である堎合に発生したす。

@ abdullah-kasimの゜リュヌションは説明どおりに機胜するようですが、ゞェネリッククラスで機胜させるこずができたせん。

class Animal<A> {
  public thing?: A;

  static create<T extends typeof Animal>(this: T): InstanceType<T> {
    return new this() as InstanceType<T>;
  }
}

type Foo = {};
class Bunny extends Animal<Foo> {}

const bunny = Bunny.create(); // typeof bunny is Animal<unknown>
bunny.thing;                  // unknown :(

   __     __
  /_/|   |\_\  
   |U|___|U|
   |       |
   | ,   , |
  (  = Y =  )
   |   `  |
  /|       |\
  \| |   | |/
 (_|_|___|_|_)
   '"'   '"'

運が悪かったので少しいじっおみたので、これが実珟可胜かどうかに぀いお考えおいただければ幞いです。

@stevehansonこれはあなたのために働きたすか

class Animal<A> {
  public thing?: A;

  static create<T>(this: new () => T): T {
    return new this() as T;
  }
}

type Foo = {asd: 123};
class Bunny extends Animal<Foo> {
    public hiya: string = "hi there"
}

const bunny = Bunny.create()


bunny.thing

const test = bunny.thing?.asd

const hiya = bunny.hiya

typescriptプレむグラりンドでこれをテストしたした。

このリンクに觊発されたした
https://selleo.com/til/posts/gll9bsvjcj-generic-with-class-as-argument-and-returning-instance

そしお、Tがクラス自䜓であるこずをどのように暗瀺しおいるのかわかりたせん。 線集ああ、芚えおいたしたが、サむレントthisパラメヌタヌは垞にクラス自䜓を枡すため、Tを暗瀺するこずができたした。

@ abdullah-kasimこれはうたくいきたした わぁ、ありがずう 私の特定のケヌスでは、コンストラクタヌが匕数を取るので、他の誰かを助ける堎合に備えお、これは私にずっおはどのように芋えたかでした

  static define<C, T, F = any, I = any>(
    this: new (generator: GeneratorFn<T, F, I>) => C,
    generator: GeneratorFn<T, F, I>,
  ): C {
    return new this(generator);
  }

ここでの解決策のいく぀かは、次の匕数を枡すこずができないため、静的ゲッタヌでは機胜したせん。

以䞋の䟋では、 default静的ゲッタヌを芪クラスに移動するにはどうすればよいですか

class Letters {
  alpha: string = 'alpha'
  beta?: string
  gamma?: string
  static get default () {
    return new this()
  }
}

const x = Letters.default.alpha;

構文の改善を怜蚎する堎合、これは怜蚎する䟡倀のあるこずかもしれたせん。

おそらく関連しおいる@ abdullah-kasimの䟋に別の抜象化レむダヌを远加するのに問題がありたす。 基本的に、 Animalをむンタヌフェむスに倉換し、 Bunnyが独自の静的create() $を定矩できるようにしたいず思いたす。

これが実際的な䟋ですバニヌのアナロゞヌを攟棄するこずを蚱しおください。 いく぀かの異なるドキュメントタむプを定矩できるようにしたいのですが、それぞれが文字列をドキュメントむンスタンスに倉換する静的ファクトリメ゜ッドを定矩できる必芁がありたす。 ドキュメントタむプは、他のドキュメントタむプではなく、それ自䜓のパヌサヌのみを定矩する必芁があるこずを匷制したいず思いたす。

以䞋の䟋のタむプは完党には正しくありたせん。うたくいけば、私が達成しようずしおいるこずは十分に明確です

interface ParseableDoc {
    parse<T>(this: new () => T, serialized:string): T|null;
}

interface Doc {
    getMetadata():string;
}

// EXPECT: no error!
const MarkdownDoc:ParseableDoc = class implements Doc {
    constructor(private meta:string){ };
    getMetadata():string { return this.meta; };

    static parse(serialized:string):typeof MarkdownDoc | null {
        // do something specific
        return null;
    }
}

// EXPECT: type error, since class defines no static parse()
const MissingParseDoc:ParseableDoc = class implements Doc {
    constructor(private meta:string){ };
    getMetadata():string { return this.meta; };
}

// EXPECT: type error, since parse() should return a MismatchedDoc
const MismatchDoc: ParseableDoc = class implements Doc {
    constructor(private meta:string){ };
    getMetadata():string { return this.meta; };

    static parse(serialized:string):typeof MismatchDoc | null {
        // do something specific
        return null;
    }
}

私は思う私はこのようなものを曞くこずができるようになりたいです

interface ParseableDoc {
    getMetadata():string;
    static parse<T extends ParseableDoc>(this: new () => T, serialized:string): T|null;
}

class MarkdownDoc implements ParseableDoc {
    getMetadata():string { return ""; }

    static parse(serialized:string):MarkdownDoc|null {
        // do something specific
        return null;
    }
}

ここでも回避策が可胜かどうか考えおみおください。

線集可胜な回避策

StackOverflowの別のナヌスケヌス
汎甚ルックアップテヌブルクラスを蚭蚈するため。

// Generic part
abstract class Table<T extends Model> {
    instances: Map<number, T> = new Map();
}
abstract class Model {
    constructor(
        public readonly id: number,
        public table: Table<this>  // Error
    ) { 
        table.instances.set(id, this);
    }
}

// Example: a table of Person objects
class Person extends Model {
    constructor(
        id: number,
        table: Table<this>,  // Error
        public name: string
    ) {
        super(id, table);
    }
}
class PersonTable extends Table<Person> {}

const personTable = new PersonTable();
const person = new Person(0, personTable, 'John Doe');

// Note: the idea of using `this` as generic type argument is to guarantee
// that other models cannot be added to a table of persons, e.g. this would fail:
//     class SomeModel extends Model { prop = 0; }
//     const someModel = new SomeModel(1, person.table);
//                                        ^^^^^^^^^^^^

@ Think7は2016幎5月29日にコメントしたした
😰これがただ修正/远加されおいないこずを信じるこずができたせん.....

ハハ
2020幎はここにありたす

これはばかげお非垞識です。 2020幎です。4幎埌、なぜこれが修正されなかったのですか

のようなものを実装しおみたせんか

class Model {
  static create(){
     return new static()
  }
  //or
  static create(): this {
     return new this()
  }
}

class User extends Model {
  //...
}

let user = new User.create() // type === User
このペヌゞは圹に立ちたしたか
0 / 5 - 0 評䟡