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

Remaining properties of a generic type can't be accessed after using spread on it #28821

Closed
que-etc opened this issue Dec 3, 2018 · 5 comments
Assignees
Labels
Bug A bug in TypeScript Domain: Conditional Types The issue relates to conditional types

Comments

@que-etc
Copy link

que-etc commented Dec 3, 2018

TypeScript Version: 3.3.0-dev.20181201

Search Terms:

  • generic
  • pick
  • exclude
  • spread

Code

type TargetProps = {
    foo: string,
    bar: string
};

const modifier = <T extends TargetProps>(targetProps: T) => {
    let {bar, ...rest} = targetProps;

    let result: typeof rest & {bar: string}

    // 'foo' doesn't exist 
    rest.foo;

    // Type 'Pick<T, Exclude<keyof T, "bar">> & { bar: string; }' is not assignable to type 'T'.
    let combined: T = result;
};

Expected behavior:
Rest properties of an object, which has a generic type, should be accessible after the spread operator was applied.

Actual behavior:
None of the properties described in the base TargetProps type can be accessed.

Playground Link: link

Related Issues:

@weswigham weswigham added Bug A bug in TypeScript Domain: Conditional Types The issue relates to conditional types labels Dec 4, 2018
@weswigham
Copy link
Member

The core of this is that a generic exclude disallows property access, even if said access is known to be "safe".

@que-etc
Copy link
Author

que-etc commented Dec 4, 2018

@weswigham I'm just curious, why the Pick/Exclude combination, when used in a conditional type, yields different result?

type TargetProps = {
    foo: string,
    bar: string
};

type Omit<T, K extends keyof T> = T extends T ? Pick<T, Exclude<keyof T, K>> : never;

const fn = <T extends TargetProps>(obj: T): void => {
    let rest: Omit<T, 'bar'>;

    // 'foo' exists.
    rest.foo;

   // Although it's still not possible to restore the type of T.
   let restored: T = {
       bar: '',
       ...rest 
   };

   // Type casting doesn't work either.
   let restored2: T = {
       bar: '',
       ...rest 
   } as T;
};

@DanielRosenwasser
Copy link
Member

The second error is reasonable; you don't know if T is instantiated with a bar that's more derived than string (e.g. a literal type).

The first error feels more questionable.

@que-etc
Copy link
Author

que-etc commented Dec 11, 2018

@DanielRosenwasser I just realized, that if some properties of the instantiated value may have a literal type, then we shouldn't be able reassign any of them.

That is, following example should probably raise an error: #28952 (comment)

P.S. sorry for linking to my own comment. Didn't really know where to ask this.

@ahejlsberg
Copy link
Member

I have a fix for the first error in the original example that I will be putting up shortly. The second error in the original example in a duplicate of #28884.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Domain: Conditional Types The issue relates to conditional types
Projects
None yet
Development

No branches or pull requests

4 participants