Skip to content

Type checking does not works for indexable type with number keys OR string keys #34611

Closed

Description

Try with newest TypeScript
TypeScript Version: 3.7.0-dev.20191018
Result: has not been fixed.

Search Terms:
typescript indexable type string keys or number keys

Code

enum PROPERTIES_TYPES {
  NUMBER = "NUMBER",
  STRING = "STRING"
  // ETC.
}

interface IStringPropertySpecification {
  type: PROPERTIES_TYPES.STRING;
  required: boolean;
  nullable?: boolean;
  newName?: string;
  emptyStringIsAllowed: boolean;
  allowedValues?: Array<string>;
  invalidValueSubstitution: string;
  defaultValue?: string;
}

interface IObjectDataSpecification__StringKeys {
  nameForLogging: string;
  properties: {
    [propertyName: string]: IStringPropertySpecification;
  };
}

interface IObjectDataSpecification__NumberKeys {
  nameForLogging: string;
  properties: {
    [propertyName: number]: IStringPropertySpecification;
  };
}

type ObjectDataSpecification = IObjectDataSpecification__StringKeys | IObjectDataSpecification__NumberKeys;


const dataSpecification: ObjectDataSpecification = {
  nameForLogging: "Test",
  properties: {
    foo: "asdfsadffsd" // Valid!!! No type errors!
  }
};

Expected behavior:

The error occurrence. I suppose, it must be:
TS2322 Type 'string' is not assignable to type ObjectDataSpecification

Actual behavior:

TypeScript ignores this error. However, my PhpStorm knows right autocompletes:

Autocomplete

Playground Link: https://jsfiddle.net/pfg1mq96/

Why I need so strange type as ObjectDataSpecification?

The ObjectDataSpecification has been created for external data validation. It's known in advance, which type external data SHOULD be, but it's unknown in advance, which data ACTUALLY WILL BE. If data satisfies ObjectDataSpecification, it will be cast as below:

const validData: IValidData = rawData as IValidData;

In above case, IValidData will be:

interface IValidData { foo: string; }

Normally, programmers should not create type like:

type ConfusingType {
  1: string;
  foo: number;
}

However I can't hope for high-quality API in the team work, The API team could define ANYTHING includes strange structure like above ConfusingType . Even it happens, I must to describe the ConfusingType with my ObjectDataSpecification. It will be:

const dataSpecification: ObjectDataSpecification = {
  nameForLogging: "ConfusingType ",
  properties: {
    1: {
      type: PROPERTIES_TYPES.STRING,
      required: true,
      emptyStringIsAllowed: false,
      invalidValueSubstitution: "---"
    },
    foo: {
      type: PROPERTIES_TYPES.NUMBER,
      required: true,
      numberType: NUMBER_TYPES.NATURAL_NUMBER,
      invalidValueSubstitution: 1
    }
  }
};

Then, my library validates external data according to dataSpecification.

Activity

  1. changed the title [-]Type checking does not works for indexable type with number OR string keys[/-] [+]Type checking does not works for indexable type with number key OR string keys[/+] on Oct 21, 2019
  2. changed the title [-]Type checking does not works for indexable type with number key OR string keys[/-] [+]Type checking does not works for indexable type with number keys OR string keys[/+] on Oct 21, 2019
  3. dragomirtitian commented on Oct 21, 2019

    @dragomirtitian
    Contributor

    A simpler repro, that has less going on:

    interface IValue {
      value: string
    }
    
    interface StringKeys { 
        [propertyName: string]: IValue;
    };
    
    interface NumberKeys {
        [propertyName: number]: IValue;
    }
    
    type ObjectDataSpecification = StringKeys | NumberKeys;
    
    
    const dataSpecification: ObjectDataSpecification = {
        foo: "asdfsadffsd" // Valid!!! No type errors!
    };

    Play

  4. selipso commented on Oct 25, 2019

    @selipso

    Is it just me or has the OR type just become f*cking broken in TypeScript 3? I don't know which version it started happening, but TypeScript 2 just felt like the best balance in providing helpful type checking without being overly anal.

    For example, error messages like these have caused me to constantly rage at Typescript because of clearly not following what "OR" is supposed to mean.

    TypeScript error in /home/thakker/Documents/GitHub/crqcentral/src/Models/helpers.ts(34,64):
    Property 'then' does not exist on type 'Promise<GraphQLResult> | Observable<object>'.
      Property 'then' does not exist on type 'Observable<object>'.  TS2339
    

    This is clearly TypeScript's fault because one of the possible return types is clearly listed as a Promise, and yet it doesn't allow me to use .then() because it picks the other type to be anal about. It treats OR as AND.

  5. RyanCavanaugh commented on Oct 30, 2019

    @RyanCavanaugh
    Member

    Yatit Thakker (@selipso) please file a separate bug if you think you've found a bug

  6. RyanCavanaugh commented on Oct 30, 2019

    @RyanCavanaugh
    Member

    Anders Hejlsberg (@ahejlsberg) I think you have a PR in progress for this?

  7. ahejlsberg commented on Nov 5, 2019

    @ahejlsberg
    Member

    Even simpler repros:

    const obj1: { [x: string]: number } | { [x: number]: number } = { a: 'abc' };  // No error!
    const obj2: { [x: string]: number } | { a: number } = { a: 5, c: 'abc' };  // No error!

    Both assignments above should be errors, but neither are. The issue is that our excess property logic doesn't properly handle union types with constituents that have index signatures.

  8. TokugawaTakeshi commented on Nov 16, 2019

    @TokugawaTakeshi
    Author

    Anders Hejlsberg (@ahejlsberg) Thank you for the fixing!
    In which version this fix will be available?

  9. 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

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