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

Unsoundness with covariant type #8718

Open
charlag opened this issue Aug 5, 2021 · 4 comments
Open

Unsoundness with covariant type #8718

charlag opened this issue Aug 5, 2021 · 4 comments

Comments

@charlag
Copy link

charlag commented Aug 5, 2021

Flow version: 0.156.0 (at least since 0.135.0)

Expected behavior

Making type generic does not introduce unsoundness.

Actual behavior

Context

Our Mithril definitions use types which looks similar to these:

export interface Vnode<Attrs> {
	attrs: Attrs,
	children: ChildArray,
	dom: HTMLElement,
}

declare interface Mithril {
	<AttrsT>(
		component: Class<MComponent<AttrsT>>,
		attributes: AttrsT,
		children?: Children
	): Vnode<any>;
}

interface MComponent<+Attrs> {
	/** Creates a view out of virtual elements. */
	view(vnode: Vnode<Attrs>): Children;
}

export type Child = Vnode<any> | string | number | boolean | null;
export type ChildArray = Array<Children>;
export type Children = Child | ChildArray;

declare var m: Mithril;

// --
// --
// --

type TestAttrs = {
	test: number,
}

class TestComponent implements MComponent<TestAttrs> {
	view(vnode: Vnode<TestAttrs>): Children {
		return String(vnode.attrs.test)
	}
}

function test(): Children {
	return m(TestComponent, {
		test: "c" // this should not be allowed
	})
}
@dsainati1
Copy link
Contributor

Yep, this is a bug. We should not allow you to make Attrs covariant in the definition of MComponent, since it appears in an input position in the interface's definition.

@charlag
Copy link
Author

charlag commented Aug 5, 2021

Ah, of course, it should be contravariant, thanks!

@charlag
Copy link
Author

charlag commented Aug 5, 2021

This might be another issue but even when I declare it as contravariant (interface MComponent<-Attrs>) it accepts things like

m(TestComponent, {})

but at least not

m(TestComponent, { another: 2 })

@jamesisaac
Copy link
Contributor

Issue from your last message is likely due to Flow treating {} as an unsealed object. You can spread null to achieve an empty object without it being unsealed: { ...null }. See #7424

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

No branches or pull requests

3 participants