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

Intuitive != expected != actual behaviour when combining string and symbol keys #26470

Description

@yortus

TypeScript Version: 3.1.0-dev.20180813

Search Terms: string index inference, string key inference

Code

// function foo takes an 'options' object that can have:
// (a) any number of string keys whose values are unary functions
// (b) an optional 'directive' unique symbol key with a unrelated type
const directive = Symbol('directive');
declare function foo<TArg, TRet, TDir>(options:
    {[x in string]: (arg: TArg) => TRet}    // all the string keys are unary functions
    & {[directive]?: TDir}                  // there is an optional 'directive' symbol key
): void;

// CASE 1
// ~~~ERROR~~~ - ... prop 'addOne' incompatible with index signature
//               ... 'x' and 'arg' are incompatible
//               ... 'string' not assignable to 'number'
let case1 = foo({
    [directive]: (x: string) => 'str',
    addOne: (x: number) => x + 1,
    double: (x: number) => x + x,
});

// CASE 2: identical to case 1 except for order of properties
// Compiles OK - infers TArg = number, TRet = number, TDir = (x: string) => string
let case2 = foo({
    addOne: (x: number) => x + 1,
    double: (x: number) => x + x,
    [directive]: (x: string) => 'str',
});

// CASE 3: identical to case 1 except for type of directive
// Compiles OK - infers TArg = number, TRet = number, TDir = string
let case3 = foo({
    [directive]: 'str',
    addOne: (x: number) => x + 1,
    double: (x: number) => x + x,
});

Intuitive expected behaviour:

All three cases compile without errors, with the string and symbol keys typed separately, since the type declaration clearly distinguishes the string keys from the symbol one.

Expected behavior according to #26257 (comment):

That comment suggests that all three cases should fail to compile, since the string index signature covers all symbol keys too, so any symbol key would have to conform to the string index signature, and they don't conform in any of the three cases in the code above.

Actual behavior:

Case 1 fails to compile, but Case 2 and 3 compile fine and in fact do type the string and symbol keys separately as intended by foo's definition.

Playground Link: here

Related Issues:
Taken directly from #26257 (comment). But the issue containing that comment is marked as 'working as intended' so I thought it would be better to open a new issue for this specific case.

Activity

  1. yortus commented on Aug 15, 2018

    @yortus
    ContributorAuthor

    Another small example from #26257:

    If {[x: string]: T} is intended to apply to all keys (i.e., strings, symbols and numbers), then shouldn't the following fail to compile, since the symbol key doesn't conform to the string index signature?

    const SYM = Symbol();
    declare function foo(obj: {[x: string]: number}): void;
    foo({a: 1, [SYM]: 'sym'});  // Compiles fine
  2. changed the title [-]Type inference problem when combining string and symbol keys[/-] [+]Intuitive != expected != actual behaviour when combining string and symbol keys[/+] on Aug 20, 2018
  3. yortus commented on Aug 20, 2018

    @yortus
    ContributorAuthor

    Ryan Cavanaugh (@RyanCavanaugh) would you mind triaging this issue please?

  4. yortus commented on Aug 20, 2018

    @yortus
    ContributorAuthor

    Thanks Ryan Cavanaugh (@RyanCavanaugh). Can we clarify what the bug is here exactly? Is it that the string index seems to include symbol keys under certain conditions, or is it that the string index doesn't include symbol keys under certain conditions?

  5. RyanCavanaugh commented on Aug 20, 2018

    @RyanCavanaugh
    Member

    I'm pretty unclear on which behavior is the desired one, which is why I was procrastinating on this one. In any case the property order really shouldn't matter, though.

  6. yortus commented on Aug 20, 2018

    @yortus
    ContributorAuthor

    I see. If it turns out that symbols must conform to string indexes, then I hope some consideration is given to how to declare things like foo where we want to type string and symbol keys separately.

    FWIW I can't think of any scenarios where you'd want a strongly-typed symbol property to be constrained by the string index. Symbol-keyed properties usually serve a very specific purpose (think of all the well-known symbols). The type of a symbol-keyed property is usually unrelated to the types of other keys in the same object. Certainly unrelated to string keys, and usually to other symbol-keyed properties too.

  7. yortus commented on Nov 6, 2018

    @yortus
    ContributorAuthor

    Hey Wesley Wigham (@weswigham), may I ask if there is something holding up the fix for this? Your PR has sat there for a couple of months now. Just wanting to know if this will be addressed by the time v3.2 comes out.

  8. weswigham commented on Nov 6, 2018

    @weswigham
    Member

    3.2 is a no at this point, given it's size, I'd guess. I'm still pushing for it, though. It's just hard to convince the relevant parties that it is both the correct fix (easy) and worth the change (hard). This issue and the others linked in the fix don't make the best case for a change of that size (not many upvotes, little obvious impact), so it's slow going.

  9. yortus commented on Nov 6, 2018

    @yortus
    ContributorAuthor

    OK. Well the PR has a much broader scope of changes that what this issue raises. Would a more limited change have more chance of making progress - eg just stop string indexes from constraining symbol keys? The other changes could possibly be approached later if there is demand.

  10. weswigham commented on Nov 6, 2018

    @weswigham
    Member

    So that's not really the issue here. The issue is that mapped types have a concept of mapping symbol keys, but no representation to map them into - so when we do inference, we end up discarding information (which is why the order matters). You need symbol index signatures to fix this.

  11. 26 remaining items

  12. added
    BugA bug in TypeScript
    and removed
    Fix AvailableA PR has been opened for this issue
    InvestigatingIs in active investigation
    SuggestionAn idea for TypeScript
    on Jan 23, 2020
  13. 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

    BugA bug in TypeScriptFix AvailableA PR has been opened for this issue

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions