schema-dts icon indicating copy to clipboard operation
schema-dts copied to clipboard

The WithContext type is invalid

Open jasongerbes opened this issue 4 years ago • 3 comments

The WithContext type accepts all Thing types:

/** Used at the top-level node to indicate the context for the JSON-LD objects used. The context provided in this type is compatible with the keys and URLs in the rest of this generated file. */
export declare type WithContext<T extends Thing> = T & {
    "@context": "https://schema.org";
};

However, Thing allows string values. For example:

export const thing: Thing = '';

It's not possible to intersect string and object, which makes the WithContext type invalid.

jasongerbes avatar Apr 24 '20 02:04 jasongerbes

Hm, I'm not sure this makes the type invalid per se, since intersections distribute over unions, WithContext<Thing> can essentially be written as:

WithContext<Thing> = WithContext<TypeA | TypeB | ... | string>
  = (TypeA | TypeB | ... | string) & {"@context": "foo"}
  = (TypeA & {"@context": "foo"}) | (TypeB & {"@context": "foo"}) | ... | (string & {"@context": "foo"})
  = (TypeA & {"@context": "foo"}) | (TypeB & {"@context": "foo"}) | ... | (never)
  = (TypeA & {"@context": "foo"}) | (TypeB & {"@context": "foo"}) | ...

We can make this more explicit by saying:

export declare type WithContext<T extends Thing> = Exclude<T, string> & {
  "@context": "https://schema.org";
};

Can I ask if this is triggering a bug or bad behavior here?

It is intended behavior for string not to be assignable to WithContext, since a top-level JSON-LD object needs to be an actual JSON-LD class type.

Eyas avatar Apr 24 '20 16:04 Eyas

Just having some trouble with the following utility function:

import { Thing, WithContext } from 'schema-dts';

export const addContextToSchema = <T extends Thing>(thing: T): WithContext<T> => {
    return {
        '@context': 'https://schema.org',
        ...thing // error: cannot be destructured as Thing may not be an object.
    };
};

This is my temporary workaround:

import { Thing, WithContext } from 'schema-dts';

// TODO: Remove this type when this issue has been resolved
// https://github.com/google/schema-dts/issues/98
export type Schema = Exclude<Thing, string>;

export const addContextToSchema = <T extends Schema>(schema: T): WithContext<T> => {
    return {
        '@context': 'https://schema.org',
        ...schema
    };
};

My hope is that you can export a variant of Thing that excludes string, and update the WithContext type to instead use that variant.

Looking at schema.org, I think these top-level 'things' are called 'types':

The vocabulary currently consists of 821 Types, 1328 Properties, and 297 Enumeration values.

So perhaps:

export declare type ThingType = Exclude<Thing, string>;

export declare type WithContext<T extends ThingType> = T & {
  "@context": "https://schema.org";
};

Or maybe:

  • ThingObject
  • RootThing
  • TopLevelThing
  • Schema

jasongerbes avatar Apr 24 '20 21:04 jasongerbes

Does this happen with just "WithSchema" or perhaps also with other things? I thought I remember something about this being the case, but regardless, it would probably be nice to limit the declarations that could get corrupted by the string inclusion.

NightScript370 avatar Jul 22 '22 00:07 NightScript370