Skip to content

Assignment doesn't have transitivity. #42479

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

Closed
5 tasks done
heroboy opened this issue Jan 25, 2021 · 6 comments
Closed
5 tasks done

Assignment doesn't have transitivity. #42479

heroboy opened this issue Jan 25, 2021 · 6 comments

Comments

@heroboy
Copy link

heroboy commented Jan 25, 2021

Suggestion

🔍 Search Terms

extends conditional constrain

✅ Viability Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

⭐ Suggestion

short example:

declare function SampleComponent(props: { a?: any; }, context?: any): React.ReactElement<any, any> | null;
declare var Tag1: React.FunctionComponent<{ a?: any; }>;
declare var Tag2: React.FunctionComponent<{}>;
declare var Tag3: typeof SampleComponent;

//Tag1 and Tag3 can assign to each other.
Tag1 = Tag3;
Tag3 = Tag1;

//Tag1 and Tag2 can assign to each other.
Tag1 = Tag2;
Tag2 = Tag1;

//But Tag2 and Tag3 can not assign to each other.
Tag2 = Tag3; //error
Tag3 = Tag2; //error

playground

📃 Motivating Example

I want to write a function to take any function component that can accept empty props.

function myrender<T extends {}>(Tag: React.FunctionComponent<T>) {
    return <Tag /> //error
}

function myrender2(Tag:React.FunctionComponent<{}>)
{
    return <Tag/>
}
myrender2(Tag3); //error

💻 Use Cases

@jcalz
Copy link
Contributor

jcalz commented Jan 26, 2021

I think other issues of assignment transitivity have been raised and closed as design limitations, like #13043 and #40529. What, specifically, is the suggestion here? In the following code, what specifically should be changed?

declare var foo: { a: string, b?: number };
declare var bar: { a: string };
declare var baz: { a: string, b?: boolean };

foo = bar; // okay
bar = foo; // okay

bar = baz; // okay
baz = bar; // okay

foo = baz; // error
baz = foo; // error

The mutual assignability of foobar and barbaz is unsound but very convenient, and breaking it would break all sorts of real-world code. Enforcing transitivity here would imply that we should allow foobaz, but who wants that? Is there something better than what TypeScript already does here?

@heroboy
Copy link
Author

heroboy commented Jan 26, 2021

@jcalz
ok , I agree with you. Thanks.

@masaeedu
Copy link
Contributor

masaeedu commented Jan 19, 2022

Is there something better than what TypeScript already does here?

IMO something better would be to distinguish between closed records, which do not admit additional fields, and open records, which can have an arbitrary set of fields besides the listed ones. I'm not sure if there's an issue for that.

This kind of unsoundness is a pretty deep issue, and shows up in the bajillions of keyof related issues for example.

@jcalz
Copy link
Contributor

jcalz commented Jan 19, 2022

"Closed records" would probably be "Exact types" as requested in #12936

@fatcerberus
Copy link

This kind of unsoundness is a pretty deep issue, and shows up in the bajillions of keyof related issues for example.

Unsound or not, it’s a fundamental part of the type system and completely by design: TS calls it structural subtyping, and it’s done that way because that’s what best models idiomatic JS. Soundness comes second. (in fact soundness is explicitly called out as not a primary pursuit in the design goals)

All that being said, #12936 is indeed rather popular as far as feature requests go; people often get to wanting it before they’re even able to articulate it. 😆

@masaeedu
Copy link
Contributor

@fatcerberus The whole point is that it isn't what "best models idiomatic JS": it is idiomatic in JS to reflect over the keys of a record and produce runtime evidence of them, and the type system produces misleading and unhelpful information when one attempts to use this pattern.

A more fine grained distinction between records not known to have additional keys and records known not to have additional keys would facilitate safe use of idiomatic JS patterns like duck typing and key enumeration.

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

4 participants