Skip to content

Instantly share code, notes, and snippets.

@pcardune
Created July 12, 2018 21:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pcardune/90ab72d85d6c1d7b7b429ac1c3c7e99f to your computer and use it in GitHub Desktop.
Save pcardune/90ab72d85d6c1d7b7b429ac1c3c7e99f to your computer and use it in GitHub Desktop.
Example of some complicated typescript typings
class Cloneable<T extends object> {
protected readonly data: T;
public static kind: string;
protected constructor(data: T) {
this.data = data;
}
static create<T extends object>(data: T) {
console.log('Creating a cloneable', this.kind);
return new this(data);
}
// this is ugly but works better than the previous.
static createWithType<T extends object, C extends Cloneable<T>>(data: T) {
console.log('Creating a cloneable', this.kind);
return <C>new this(data);
}
clone() {
return new (<typeof Cloneable>this.constructor)(this.data);
}
// This is ugly, but works better than the previous. Why is this necessary?
cloneWithType<C extends Cloneable<T>>(): C {
return <C>new (<typeof Cloneable>this.constructor)(this.data);
}
}
interface PersonData {
name: string;
}
class Person extends Cloneable<PersonData> {
static kind = 'Person';
getName() {
return this.data.name;
}
}
// Why doesn't this just work?
// Type 'Cloneable<{ name: string; }>' is not assignable to type 'Person'.
// Property 'getName' is missing in type 'Cloneable<{ name: string; }>'.
const paul: Person = Person.create({ name: 'Paul' });
// This is one way to fix it, but it's ugly.
const christophe: Person = Person.createWithType<PersonData, Person>({
name: 'Christophe'
});
// Why doesn't this just work?
// Type 'Cloneable<PersonData>' is not assignable to type 'Person'.
// Property 'getName' is missing in type 'Cloneable<PersonData>'.
const paul2: Person = paul.clone();
// This seems to work but makes the cloneWithType definition quite complicated.
const christophe2: Person = christophe.cloneWithType();
console.log(paul.getName());
console.log(paul2.getName());
console.log(christophe.getName());
console.log(christophe2.getName());
@wjohnsto
Copy link

wjohnsto commented Jul 13, 2018

The create issue is a common problem: microsoft/TypeScript#5863

You probably just have to do const paul = <Person>Person.create({ name: 'Paul' });

For the clone maybe something like this would work?

clone(): this {
    return <this>new (<typeof Cloneable>this.constructor)(this.data);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment