Skip to content

Can't assign existing property to the rest of generic #28952

Closed

Description

TypeScript Version: 3.2.2 and 3.3.0-dev.20181208

Search Terms:

  • generic
  • pick
  • exclude
  • spread

Code

interface AB {
  a: string;
  b: string;
}

function excludeA<T extends AB>(obj: T) {
  const {a, ...rest} = obj;

  rest.b = '3';

  return rest;
}

excludeA({ a: '1', b: '2', c: 'test' });

Expected behavior:
No errors

Actual behavior:
Property 'b' does not exist on type 'Pick<T, Exclude<keyof T, "a">>'.

Playground Link:
https://www.typescriptlang.org/play/index.html#src=interface%20AB%20%7B%0D%0A%20%20a%3A%20string%3B%0D%0A%20%20b%3A%20string%3B%0D%0A%7D%0D%0A%0D%0Afunction%20excludeA%3CT%20extends%20AB%3E(obj%3A%20T)%20%7B%0D%0A%20%20const%20%7Ba%2C%20...rest%7D%20%3D%20obj%3B%0D%0A%0D%0A%20%20rest.b%20%3D%20'3'%3B%0D%0A%0D%0A%20%20return%20rest%3B%0D%0A%7D%0D%0A%0D%0AexcludeA(%7B%20a%3A%20'1'%2C%20b%3A%20'2'%2C%20c%3A%20'test'%20%7D)%3B

Related
#28821

Activity

  1. weswigham commented on Dec 11, 2018

    @weswigham
    Member

    Pavel Klimashkin (@klimashkin) - Consider:

    interface AB {
      a: string;
      b: string;
    }
    
    function excludeA<T extends AB>(obj: T) {
      const {a, ...rest} = obj;
    
      rest.b = '3';
    
      return rest;
    }
    
    interface ExactlyAB extends AB {
      a: "a";
      b: "b"
    }
    
    declare var exactAB: ExactlyAB;
    
    excludeA(exactAB).b; // claims to be of type "b", but will be the value "3" with your implementation 
  2. weswigham commented on Dec 11, 2018

    @weswigham
    Member

    (But in general yes, #28821 is the reason why the assignment doesn't work, even when you correctly assign a T["b"] - I just wanted to let you know that that code is unsafe from a typesystem perspective, even once that bug is fixed)

  3. que-etc commented on Dec 11, 2018

    @que-etc

    Wesley Wigham (@weswigham)
    Does it mean that any reassignment of an object's property, which has a generic type, should be considered invalid?

    const test = <T extends {foo: string}>(obj: T): T => {
        // No errors as for v3.2.2
        obj.foo = 'bar';
    
        return obj;
    };

    Playground.

  4. weswigham commented on Dec 12, 2018

    @weswigham
    Member

    Yea - it's a known hole that gets closed when you use indexed access types (which get automatically made when you fetch properties off mapped types). It's quite troublesome, really, since the only reason we haven't fixed it, AFAIK, is for backwards compatibility (and to make mutating patterns "easier" to type).

  5. typescript-bot commented on Dec 13, 2018

    @typescript-bot
    Contributor

    This issue has been marked as a duplicate and has seen no activity in the last day. It has been closed for automatic house-keeping purposes.

  6. locked as resolved and limited conversation to collaborators on Oct 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions