schema-dts
schema-dts copied to clipboard
Cannot 'implement' Person from class
When I try to implement 'Person' on a class like:
import { Person as PersonSchema } from 'schema-dts';
export class Person implements PersonSchema { ... }
TS errors with
A class can only implement an object type or intersection of object types with statically known members.
Which can be solved by editing this snippet:
export declare type Person = ({
"@type": "Person";
} & PersonBase) | Patient;
to
export declare type Person = ({
"@type": "Person";
} & PersonBase);
Obviously, this case is true for any type declaration in the pattern (...) | [TYPE]
. The same applies to extending interfaces.
What is the reason for this pattern? Is it intended?
I describe the reasoning briefly here. The quick gist is that we'd like to model super-classes as discriminated unions of their subclasses because:
-
Patient
is a sub-class ofPerson
, so a property whose range includesPerson
can have a value with"@type": "Patient"
in it as well. In any place where a super-class is expected, it's sub-class should be expected as well. The sub-class would have a more specific"@type"
. Removing the union means that only"@type"
is expected. -
Removing the union also means that extra properties on
"Patient"
will not be inferred, since TypeScript will only type-check the known properties, finally -
Removing the union also means that the demo in the README.md gif, where we can see completions on
"@type"
for an Organization (School, Airline, etc.) wouldn't work.
For what you're trying to do, some ideas:
-
The least invasive is for your classes to implement providers, e.g. with a 'toJson(): Person' method or similar. But I get that this might be less appealing.
-
We could start exporting the "leaf"/"base" class. So you can't implement
Person
still, but you can implementPersonLeaf
which is basically{"@type": "Person" } & PersonBase
.
Generally I'd like not to export too many things, because that increases the API surface area and increases the risk of someone depending on some internal property/implementation that might not always last. So I'd like to understand a bit what your use case is.
If this is something compelling and a lot of people might be interested in doing, then exporting PersonLeaf
, etc. might make sense.
Thank you for the resource and explanation. Been playing around with Thing, Person and Patient. It kind of makes sense to me now.
Was indeed trying to implement Schema.org on the scope of a class since it seems the most optimal way for me to follow Schema.org, hopefully avoid naming conflicts and be able to build on it's conventions & patterns.
Might be nice to have a 'schema-dts-leaf' version to seperate and use it for implements. I guess you'd still need methods to export (or import) the data into the class, but at least it can all live at the top level.