Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

compile error in 2.7 for indexed types work with generics #21529

Closed
tommytroylin opened this issue Feb 1, 2018 · 8 comments
Closed

compile error in 2.7 for indexed types work with generics #21529

tommytroylin opened this issue Feb 1, 2018 · 8 comments
Labels
Duplicate An existing issue was already created

Comments

@tommytroylin
Copy link

TypeScript Version: 2.7.1

Search Terms: typer inference, indexed types, generics

Code

type Foo = {
  [name: string]: string[] | undefined;
};

const foo: Foo = {};
function bar<SomeName extends string>(name: SomeName): void {
  foo.any!.concat(); // ok
  foo[name]!.concat(); // Property 'concat' does not exist on type 'Foo[SomeName]'
  const newFoo = foo[name]; // ok
  newFoo!.concat(); // ok em....
}

with tsconfig (removed other configs)

{
  "compilerOptions": {
    "esModuleInterop": true,
    "strict": true,
    "strictPropertyInitialization": false
  }
}

Expected behavior:
No Error in 2.7 & 2.6

Actual behavior:
No Error in 2.6
but in 2.7 it throws Property 'concat' does not exist on type 'Foo[SomeName]'

FYI. I found some ways to make this error disappear. I think there must be something related but I can't tell.

  • remove undefined type in Foo 's value
  • use a temp var to avoid directly usage like const newFoo = foo[name]
  • remove generics and define param name to be string
@tommytroylin
Copy link
Author

confirmed it exists on version 2.8.0-dev.20180201

@ajafff
Copy link
Contributor

ajafff commented Feb 1, 2018

Maybe related to #21368

@mhegazy
Copy link
Contributor

mhegazy commented Feb 1, 2018

looks like a duplicate of #21369

@mhegazy mhegazy added the Duplicate An existing issue was already created label Feb 1, 2018
@tommytroylin
Copy link
Author

@mhegazy

I 've read your post on #21369

With that in mind, previously, in TS 2.6 and before, the logic for getting a constraint on a indexed access type was if it has an index signature, then use that, otherwise it was any. In the example above, o[k] was constrained to any, then applying the null assertion operator had no effect, and thus was assignable to string in that case. That means that also take(o[k]) would have been permissible.

but it's not the same case i think.

in ts 2.6.2, the compiler seems to be able to infer that type correctly, not any but string[]

like Foo[SomeName] => string[] | undefined => null assertion operater => string[]

image

in ts 2.7.1 with a bit smarter compiler, nothing . xD

like Foo[SomeName] => string[] | undefined => null assertion operater => string[] | undefined
image

BTW, I notice that you labeled #21369 with break change. Is it means won't fix and will document it ? (Nothing related to that issue, just break change label.)

@tommytroylin
Copy link
Author

playground
should turn on strictNullCheck as well

@tommytroylin
Copy link
Author

considering

  • error still exists when replacing undefined with null
  • error disappears when strictNullChecks turned off
  • error disappears when using a temp var (maybe forcing to do a type inference?)

In my opinion, it's a bug in type system

@mhegazy
any updates on this issue?

@Jessidhia
Copy link

Jessidhia commented Feb 13, 2018

This seems to be a problem with the non-null assertion operator when used on the result of a computed member expression. This problem was not present on 2.7.0-rc; seems to be new to 2.7.1.

import * as types from './types'

interface PreloadTypes {
  post: types.PreloadPostData
  user: types.PreloadUserData
}

interface GlobalPreload {
  post?: { [id: string]: PreloadTypes['post'] }
  user?: { [id: string]: PreloadTypes['user'] }
}
declare var preload: GlobalPreload

export function getPreload<TKind extends keyof PreloadTypes> (kind: TKind, id: string): PreloadTypes[TKind]|undefined {
  if (kind in preload && id in preload[kind]) {
    return preload[kind]![id]
    // [id] access errors because it's indexing a possibly undefined object
  }
}

Changing the line to, for example, be preload.user![id] does not produce an undefined object access error.

Changing the access to be

const kindPreload = preload[kind]!
return kindPreload[id]

instead still causes a build error on the second linke because kindPreload may be undefined, despite the typeof kindPreload correctly showing up as GlobalPreload[TKind] on hover type info. It's only when it is then indexed that the | undefined shows up.

Finally, adding the ! on the [id] access when it's split into multiple vars works -- return kindPreload![id] will compile.

Also note that removing the ! from preload[kind]! does not produce | undefined on the typeof kindPreload; in fact leaving it in there triggers tslint's no-unnecessary-type-assertion if you have it enabled.

@typescript-bot
Copy link
Collaborator

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

@microsoft microsoft locked and limited conversation to collaborators Jul 3, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

5 participants