Typescript: Incorrectly inferred object property as string literal value

Created on 18 Jun 2020  路  3Comments  路  Source: microsoft/TypeScript

TypeScript Version: 3.9.2


Search Terms:
incorrectly inferred string index type
inferred object property in function return type

Expected behavior:
The key values in each Variation should be inferred as string not the literal string value

Actual behavior:
The key values in each Variation are inferred as the literal string value of the first object


Related Issues:
Possibly related: https://github.com/microsoft/TypeScript/issues/38759,

Code

type Variation = {
    [testDescription: string]: () => void;
}

export type VariationProvider = {
  variations: Variation[];
};

export type VariationProviderFactory = (extras: object) => VariationProvider;

export function createVariationProvider(factory: VariationProviderFactory) {
  return factory;
}


createVariationProvider(() => {
  return {
    variations: [
      {
        foo: () => {}
      },
      {
        broken: () => {}
      },
    ]
  }
})

Output

export function createVariationProvider(factory) {
    return factory;
}
createVariationProvider(() => {
    return {
        variations: [
            {
                foo: () => { }
            },
            {
                broken: () => { }
            },
        ]
    };
});

Compiler Options

{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "strictBindCallApply": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "useDefineForClassFields": false,
    "alwaysStrict": true,
    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    "downlevelIteration": false,
    "noEmitHelpers": false,
    "noLib": false,
    "noStrictGenericChecks": false,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "esModuleInterop": true,
    "preserveConstEnums": false,
    "removeComments": false,
    "skipLibCheck": false,
    "checkJs": false,
    "allowJs": false,
    "declaration": true,
    "experimentalDecorators": false,
    "emitDecoratorMetadata": false,
    "target": "ES2017",
    "module": "ESNext"
  }
}

Playground Link: Provided

Needs Investigation

Most helpful comment

A more distilled version

type VariationProviderFactory = () => Record<string, () => void>[]

const variationProviderFactory: VariationProviderFactory = () => [
  {
    foo: () => { }
  },
  {
    broken: () => { }
  },
]


const vars: Record<string, () => void>[] = [
  {
    foo: () => { }
  },
  {
    broken: () => { }
  }
];

const variationProviderFactory2: VariationProviderFactory = () => vars

All 3 comments

@ahejlsberg the optional object literal property detection logic isn't quite doing the right thing here

A more distilled version

type VariationProviderFactory = () => Record<string, () => void>[]

const variationProviderFactory: VariationProviderFactory = () => [
  {
    foo: () => { }
  },
  {
    broken: () => { }
  },
]


const vars: Record<string, () => void>[] = [
  {
    foo: () => { }
  },
  {
    broken: () => { }
  }
];

const variationProviderFactory2: VariationProviderFactory = () => vars

The object literal

[
    { foo: () => {} },
    { broken: () => {} }
]

has the widened type

[
    { foo: () => void, broken?: undefined },
    { broken: () => void, foo?: undefined }
]

The optional properties are a result of object literal normalization (#19513), which is part of type widening. So, we have the unfortunate situation that the unwidened form of the object literal is assignable to Record<string, () => void>, whereas the widened form of the object literal isn't. The examples then simply reveal when widening is or isn't performed. And, basically, we widen function return expression types unless the containing function has an explicit type annotation--and the examples don't.

Anyway, I think the best fix is to change the Record<string, () => void> type to Record<string, (() => void) | undefined> which really is more correct. You could even argue that we're finding a potential bug, except we're not particularly consistent about it.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

siddjain picture siddjain  路  3Comments

remojansen picture remojansen  路  3Comments

weswigham picture weswigham  路  3Comments

jbondc picture jbondc  路  3Comments

MartynasZilinskas picture MartynasZilinskas  路  3Comments