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

Types fulfill an interface, but interfaces do not #41518

Closed
zachrip opened this issue Nov 12, 2020 · 4 comments
Closed

Types fulfill an interface, but interfaces do not #41518

zachrip opened this issue Nov 12, 2020 · 4 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@zachrip
Copy link

zachrip commented Nov 12, 2020

TypeScript Version: 3.7.x-dev.201xxxxx

Search Terms:
Constrain type to a specific set of types, serializable values, nested types
Code

This version does not work:

export type BaseTypes<P> = string | number | boolean | null | P;
export interface ICacheableObject<P> extends Record<string, Cacheable<P>> {}

export type Cacheable<P = never> =
  | BaseTypes<P>
  | BaseTypes<P>[]
  | ICacheableObject<P>
  | ICacheableObject<P>[];

interface SimpleUserGroup {
  group_id: string;
}

// no extra classes, just basic primitives/arrays/objects
interface UserInfo {
  user_id: string;
  groups: SimpleUserGroup[];
}

// the objective is for this to be allowed:
const data: Cacheable = {
  user_id: 'test',
  groups: [],
} as UserInfo;


class Foo { test = 'abc' }
// but not this
const otherData: Cacheable = {
  foo: new Foo()
};

This version does work (using types instead of interfaces):

export type BaseTypes<P> = string | number | boolean | null | P;
export interface ICacheableObject<P> extends Record<string, Cacheable<P>> {}

export type Cacheable<P = never> =
  | BaseTypes<P>
  | BaseTypes<P>[]
  | ICacheableObject<P>
  | ICacheableObject<P>[];

type SimpleUserGroup {
  group_id: string;
}

// no extra classes, just basic primitives/arrays/objects
type UserInfo {
  user_id: string;
  groups: SimpleUserGroup[];
}

// the objective is for this to be allowed:
const data: Cacheable = {
  user_id: 'test',
  groups: [],
} as UserInfo;


class Foo { test = 'abc' }
// but not this
const otherData: Cacheable = {
  foo: new Foo()
};

Expected behavior:
I expect that UserInfo can be a valid value when compared to Cacheable as it only contains basic strings/arrays. The intended use case is to create a type safe serializer.

Actual behavior:
When using interfaces, typescript wants UserInfo to have an index or to be a type instead of interface.

Playground Link:
Not working: https://www.typescriptlang.org/play?#code/KYDwDg9gTgLgBDAnmYcBCBDAzsAKs4LAHgAUA+OAXjixigEsA7AczgB85GBXAWwCNgUdnD4QIAG2AZGw7uPHCSAbgCwAKFCRYcJjEEAzDAGNUASQDCxgBZS+kgPJ8AVsCMxSFUHsYATLHAAlV2gfIloGFgAaOEsjGww7YA8KAG8AX3V1TWh4JBQY61tJUipOYAA3QQpKdThhTBx8FGJyWvrsPAIWsgBtAF02jgtChIdnV3dWtTqh2PjExxc3D37VNXVdA2NUAGV6HjBJAFUcKABxKAguMDgUtuZL64B9eh8ALho6JmY1jPW1AD0AM4EDgXigGDgRnE2BwWGiTi4tBE2HoRjgYAYPHoMHolSwAIwUAhiAJEHGbiwG0YeighhMcBOglMjH0oLu0zgSMEL3enwiP3ujzAWA+ewOx1OFyuYFW6j+6iBCBscHJS1xlR0-jZQhgVno-hgoIEcAw8ggAHdgO91EYIIxkT4MDAMB85kVUNQOXVuVBeR8AOR6WgByJCmWiuD9MNqNKm-xMqAstlrTJqaGwuAAMTEtwQhHg1ADCSMAbgfyVfC48EYEFy+qp6ftyLrNigABFna6CnEPaVvXA2RAPoxgBbs2IABQASnlSiAA

Working: https://www.typescriptlang.org/play?#code/KYDwDg9gTgLgBDAnmYcBCBDAzsAKs4LAHgAUA+OAXjixigEsA7AczgB85GBXAWwCNgUdnD4QIAG2AZGw7uPHCSAbgCwAKFCRYcJjEEAzDAGNUASQDCxgBZS+kgPJ8AVsCMxSFUHsYATLHAAlV2gfIloGFgAaOEsjGww7YA8KAG8AX3V1TWh4JBQY61tJUipOYAA3QQpKdThhTBx8FGJyWvrsPAIWsgBtAF02jgtChIdnV3dWtTqh2PjExxc3D37VNXU81ABleh4wSQBVHCgAcSgILjA4FLbmc8uAfXofAC4aOiZmNYz1tQB6P6cCBwLxQDBwIzibA4LDRJxcWgibD0IxwMAMHj0GD0SpYP4YKBgxB4iDjNxYDYEOBHQSmRj6YE3aZwBGCJ6vd4RL63e5gLBvHZ7Q7HM4XMCrdQ-dQAhA2OCkpbYyo6fwMoQwKz0fwwYECOAYeQQADuwFe6iMEEYiJ8GBgGDecyKqGoTLqrKg7LeAHI9LQvZEeWL+XB+gG1Gl9f4aVA6Qy1pk1JDoXAAGJia4IQjwahehJGL1wH4yvhceCMCC5TUUxOWxEVmxQAAitvtBTiTtKrrgDIgb0YwCNqbEAAoAJSSpRAA
Related Issues:

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Nov 12, 2020
@RyanCavanaugh
Copy link
Member

Type aliases can implicitly fulfill an index signature (which is being declared at the Record<string> usage), but not interfaces (since they're subject to declaration merging)

@typescript-bot
Copy link
Collaborator

This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

@DanielRosenwasser
Copy link
Member

Design notes at #7059

@jcalz
Copy link
Contributor

jcalz commented May 6, 2022

Crosslinking #15300

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

5 participants