A
arktype•16mo ago
jacksteamdev

How to validate that a value is an instance of a class

Is there a way (yet) to validate that a value is an instance of a class? I know this doesn't work, but maybe it'll convey the idea:
class SomeClass {}
const someType = type(SomeClass)
someType.allows("abc") // false
someType.allows(new SomeClass()) // true
class SomeClass {}
const someType = type(SomeClass)
someType.allows("abc") // false
someType.allows(new SomeClass()) // true
13 Replies
jacksteamdev
jacksteamdevOP•16mo ago
I came up with something like this:
type(["any", "|>",
(x): SomeClass => {
if (x instanceof SomeClass) return self;
throw new Error("must be a SomeClass instance");
}
]);
type(["any", "|>",
(x): SomeClass => {
if (x instanceof SomeClass) return self;
throw new Error("must be a SomeClass instance");
}
]);
Dimava
Dimava•16mo ago
type(['instanceof', SomeClass])
type(['instanceof', SomeClass])
afaik
ssalbdivad
ssalbdivad•16mo ago
@Dimava's solution should work. There is also an instanceOf helper method, though the API for that will likely change since all the other helper methods are changing so the tuple expresion is probably the most reliable going forward.
jacksteamdev
jacksteamdevOP•16mo ago
Nice, thanks!
jacksteamdev
jacksteamdevOP•16mo ago
here's a fun one with private properties: https://stackblitz.com/edit/arktype-bug-rxgdqa?file=demo.ts
import { type } from 'arktype';

class Foo {
private bar = 'baz';
}

function fn(foo: Foo) {
// ....
}

const Foo1 = type(['instanceof', Foo]).assert(new Foo());
fn(Foo1); // Type Error: Argument of type '{}' is not assignable to parameter of type 'Foo'.
// Property 'bar' is missing in type '{}' but required in type 'Foo'.(2345)

const Foo2 = type([
'unknown',
'|>',
(value: unknown): Foo => {
if (value instanceof Foo) return value;
throw new Error('value must be an Foo instance');
},
]).assert(new Foo());
fn(Foo2);
import { type } from 'arktype';

class Foo {
private bar = 'baz';
}

function fn(foo: Foo) {
// ....
}

const Foo1 = type(['instanceof', Foo]).assert(new Foo());
fn(Foo1); // Type Error: Argument of type '{}' is not assignable to parameter of type 'Foo'.
// Property 'bar' is missing in type '{}' but required in type 'Foo'.(2345)

const Foo2 = type([
'unknown',
'|>',
(value: unknown): Foo => {
if (value instanceof Foo) return value;
throw new Error('value must be an Foo instance');
},
]).assert(new Foo());
fn(Foo2);
StackBlitz
class instance validation - StackBlitz
ArkType demo demo
ssalbdivad
ssalbdivad•16mo ago
Oh fun 😅 Actually I think due to @Dimava's suggestion in the next release it will infer instance types like that directly without mapping so that should resolve the issue. It seems pretty doomed until then because it will never map over that prop- even if you cast it, the mapping step is last so you'd have to cast it outside ArkType 🥹
jacksteamdev
jacksteamdevOP•16mo ago
lol, at least there's a workaround, no matter how ugly 👹 do you want an issue for it, or do you think it's covered already?
ssalbdivad
ssalbdivad•16mo ago
Ugh I don't know if what I'm looking at is broken because I have a bunch of broken imports on my branch 😅 I'm 100% sure it worked though, I'll just create a simple reminder issue no need for repro or anything Actually I'll just make sure there's a unit test for it Ahh wtf The private properties actually do mess it up
ssalbdivad
ssalbdivad•16mo ago
No description
No description
ssalbdivad
ssalbdivad•16mo ago
Hmm okay I get it, because we map over the type it's not longer assignable so it thinks it's a morph
No description
ssalbdivad
ssalbdivad•16mo ago
I think we can fix this by reversing the check
ssalbdivad
ssalbdivad•16mo ago
This works, I'm just trying to think if there are other implications
No description
ssalbdivad
ssalbdivad•16mo ago
This breaks inference for morphs returning something like unknown Well I have a failing test case and a note to follow up so I'll either resolve it or create a real issue. If the perf cost of checking bidirectionally is too high, another potential solution would be to use the global type config @ecyrbe mentioned to specify more non-narrowed objects like TerminallyInferredObjectKind So we'll figure something out 🙂
Want results from more Discord servers?
Add your server