Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot 'implement' Person from class #28

Open
vandijkstef opened this issue Aug 19, 2019 · 2 comments
Open

Cannot 'implement' Person from class #28

vandijkstef opened this issue Aug 19, 2019 · 2 comments

Comments

@vandijkstef
Copy link

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?

@Eyas
Copy link
Collaborator

Eyas commented Aug 19, 2019

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:

  1. Patient is a sub-class of Person, so a property whose range includes Person 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.

  2. Removing the union also means that extra properties on "Patient" will not be inferred, since TypeScript will only type-check the known properties, finally

  3. 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 implement PersonLeaf 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.

@vandijkstef
Copy link
Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants