镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

Inconsistent property type inference #49463

Description

@tippfelher

Section A causes no TS error in the source code below.
Section B causes a TS error in the source code below.
Both sections do the same thing and section B shouldn't yield any errors. (?)
The message "Type 'boolean' is not assignable to type 'string'" also makes no sense because prop is clearly a boolean now. (?)

(?) means: I am pretty sure, but not entirely sure, as I am new to Typescript. ^^

type Template = {
  prop: number;
}

type Keys<Type> = keyof Type | (string & Record<never, never>);

type SomeType<T> = {
  [Property in Keys<T>]?: Property extends keyof T ? boolean : string;
};

// section A: no TS error
let something: SomeType<Template> = {};
something.prop = true;
something.anotherProp = 'value';

// section B: TS error
// Type '{ prop: true; anotherProp: string; }' is not assignable to type 'SomeType<Template>'.
//   Property 'prop' is incompatible with index signature.
//   Type 'boolean' is not assignable to type 'string'
something = {
  prop: true,
  anotherProp: 'value',
};

Here is the playground.

Activity

  1. RyanCavanaugh commented on Jun 10, 2022

    @RyanCavanaugh
    Member

    Simpler version:

    type Template = {
      prop: number;
    }
    
    type SomeType<T> = {
      [Property in keyof T]?: boolean;
    } & { [s: string]: string };
    
    // section A: no TS error
    let something: SomeType<Template> = {};
    something.prop = true;
    something.anotherProp = 'value';
    
    // section B: TS error
    // Type '{ prop: true; anotherProp: string; }' is not assignable to type 'SomeType<Template>'.
    //   Property 'prop' is incompatible with index signature.
    //   Type 'boolean' is not assignable to type 'string'
    something = {
      prop: true,
      anotherProp: 'value',
    };

    It seems like you're trying to emulate rest index signatures, which aren't really supported. See #7765.

  2. tippfelher commented on Jun 11, 2022

    @tippfelher
    Author

    That's very, very sad because this sounds like a very useful feature when working with types. (at least in my use cases ^.-)

    So basically, in order to achieve this, I have to generate a separate file with type definitions from runtime information as relay does.

    Thank you very much though.

  3. fatcerberus commented on Jun 11, 2022

    @fatcerberus

    The problem with “rest” properties is, given you have { [x: string]: string, foo: number }, what should the type system do when given an arbitrary string key that happens (at runtime) to be "foo"? That’s not something the type system can verify at compile time, so TS doesn’t even try to model this case and instead treats the index signature as universal.

    Note that you have to be very careful when using this pattern at runtime for the same reason - if your keys are dynamic (provided by the user, e.g.), you have to make sure they don’t overlap with the named ones.

  4. tippfelher commented on Jun 11, 2022

    @tippfelher
    Author

    I am not saying { [x: string]: string, foo: number } should work because this would obviously be bad design. But if you write it as

    {
      [x in any string but 'foo']: string; // (pseudo syntax)
      foo: number;
    }
    

    then there would be no problem to determine the type for property baby or foo. (This prolly cannot be implemented in the current TS architecture)

    Edit: I wanted to make use of the (non-existing, because I didn't know it better) rest feature with recursion but recursion doesn't seem to work even if rest properties were supported.

  5. fatcerberus commented on Jun 11, 2022

    @fatcerberus

    Well, { foo: number } & { [x: string]: string } (roughly what you have in the OP) is basically the same type as { [x: string]: string, foo: number } and neither one is safe for the above reasons, is what I was getting at. Your pseudocode above would indeed be a solution, but there's currently no way to express "any string except 'foo'" - that would require Negated Types, which, judging by the fact that PR was recently closed, looks like have been taken off the table. 😢

  6. fatcerberus commented on Jun 11, 2022

    @fatcerberus

    I am not saying { [x: string]: string, foo: number } should work because this would obviously be bad design.

    Yep, I agree - but you'd be surprised how many people still want that exact type to work, and will argue vehemently with you when you try to tell them it's unsafe. It's refreshing to see someone who recognizes that. 😄

  7. locked as resolved and limited conversation to collaborators on Oct 22, 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