Reputation: 1
I have the following type and class:
export type ProductType = {|
id: number,
name: string,
slug: string,
tooltip: string,
properties?: string[]
|};
class Product {
id: number,
name: string,
slug: string,
tooltip: string,
properties?: string[]
constructor(props: $Shape<ProductType>) {
this.id = props.id;
this.name = props.name || '';
this.slug = props.slug || '';
this.tooltip = props.tooltip || '';
this.properties = props.properties || [];
}
}
and I'd like to be able to do something like this:
const product: ProductType = new Product({ name: 'test' });
but flows complain saying the following:
Cannot assign `new Product()` to `product` because inexact `Product` [1] is incompatible with exact `ProductType`
so I'd like to know if there is any way to return an exact/freeze/seal object from the class constructor or if this is even possible and if not what other alternatives I have.
Upvotes: 0
Views: 1026
Reputation: 4086
Unfortunately, class instances are inexact. Here's an illustration:
class Foo {
foo: string;
constructor() {
this.foo = 'foo';
}
}
class Bar extends Foo {
bar: string;
constructor() {
super();
this.bar = 'bar';
}
}
type JustFoo = {| foo: string |};
const x: Foo = new Bar();
// Expected error -- if Flow allowed this, the exact type would be a lie!
const y: JustFoo = x;
const z: JustFoo = { foo: "foo" };
In this case, Bar
extends Foo
and adds an additional property. Because classes are nominally typed, Bar
is a subtype of Foo
. Therefore, Foo
cannot be a subtype of {| foo: string |}
without breaking the type system.
The recommended way to create an exact object is to just write an object literal, like I did for z
in this example.
Upvotes: 3