#conditional types generic class

22 messages · Page 1 of 1 (latest)

formal dragon
#

Hi.
Currently i found out typescript has conditional types! Its awesome.
I can do something like this

T extends {} ? {[K in keyof T]: T[K]}
: T

it it possible to only get classes? (not a specific class, just any object that creates by a class, but no {} objects)

T extends ClassObject ? {[K in keyof T]: T[K]}
: T
sinful oak
#

no. in javascript instances of classes are just objects (usually they have a prototype chain set up, but you can also do that without classes and in any case it's not something the type system models)

#

if you can explain more about what specific problem you are trying to solve then i/someone else might have suggestions

tropic ventureBOT
#

@untold bramble Here's a shortened URL of your playground link! You can remove the full link from your message.

bmkotu#7304

Preview:```ts
type IsClassInstance<T> = T extends InstanceType<any> ? true : false;

type ClassProperties<T> = IsClassInstance<T> extends true
? { [K in keyof T]: T[K] }
: T;

// Usage
class MyClass {
prop1 = '';
prop2 = 1;
}

type MyClassProperties = ClassProperties<MyClass>; // This will have prop1 and prop2
...```

untold bramble
#

you can use InstanceType to check if its an instance of something, which only applies to classes I think

sinful oak
#

that doesn't work:

tropic ventureBOT
#
type IsClassInstance<T> = T extends InstanceType<any> ? true : false;

type X = IsClassInstance<string>
//   ^? - type X = true
sinful oak
#

check the definition of InstanceType. it expects to be provided a type that has a construct signature and just evaluates to the instance type the constructor returns

#

but any "turns off" typechecking and is infectious as usual, so InstanceType<any> is just any

#

however even if you used a specific class type there, it wouldn't do what they want:

tropic ventureBOT
#
class Foo { foo = '' }

type IsFooInstance<T> = T extends InstanceType<typeof Foo> ? true : false;

const fooInstance = new Foo()
type X = IsFooInstance<typeof fooInstance>
//   ^? - type X = true

const fooObject = { foo: '' } // not a class instance
type Z = IsFooInstance<typeof fooObject> // but this is still `true`
//   ^? - type Z = true
untold bramble
#

hmm ok thanks for the clarification

formal dragon
#

oh :O i got an answer! So its not possible i see.

formal dragon
#

i found out you can use something like this

#
type IsClassInstance<T> = T extends Record<string, infer _> ? false : true

const fooInstance = new Foo()
type X = IsClassInstance<typeof fooInstance>
//   ^? - type X = true

type Bla = { foo: string };

type Z = IsClassInstance<{ foo: string }>
//   ^? - type Z = false
#

infer _ is here important

#

But there some other issues with this

#
class Foo { foo = '' }

interface FooInterface { foo:string }

type IsClassInstance<T> = T extends Record<string, infer _> ? false : true

const fooInstance = new Foo()
type X = IsClassInstance<typeof fooInstance>
//   ^? - type X = true

const fooObject = { foo: '' };
type Z = IsClassInstance<typeof fooObject>
//   ^? - type Z = false

const fooInt:FooInterface = { foo: '' };
type D = IsClassInstance<typeof fooInt>
//   ^? - type X = true meeeh!
#

So technically its important that its everything that can construct this way:

var = {}

und not this way

var = new Class();

sinful oak
#

IsClassInstance isn't really checking if it's a class instance (as you discovered), but rather whether the type has a (potentially implicit) index signature. in general you'll get false for type aliases and true for interfaces

#

IMO you should avoid fighting against the structural nature of typescript's type system. if an object has a foo property it shouldn't matter whether it was created via new or not