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

Error: Type cannot be used to index type after two indexes #21760

Open
tenorok opened this issue Feb 8, 2018 · 14 comments
Open

Error: Type cannot be used to index type after two indexes #21760

tenorok opened this issue Feb 8, 2018 · 14 comments
Labels
Bug A bug in TypeScript
Milestone

Comments

@tenorok
Copy link

tenorok commented Feb 8, 2018

TypeScript Version: 2.7.1, 2.8.0-dev.20180208

Search Terms:
Type cannot be used to index type

Code

interface IExample {
    foo: {
        bar: {
            baz: number;
        }
    }
}

type F = <
    name extends keyof IExample,
    val extends keyof IExample[name]
>() => IExample[name][val]['baz']; // ← Type '"baz"' cannot be used to index type 'IExample[name][val]'.

Expected behavior:
In version 2.7.0-dev.20171115 this code was checking without errors.

Actual behavior:
Now in throws error: Type '"baz"' cannot be used to index type 'IExample[name][val]'.

Playground Link:
https://www.typescriptlang.org/play/index.html#src=interface%20IExample%20%7B%0D%0A%20%20%20%20foo%3A%20%7B%0D%0A%20%20%20%20%20%20%20%20bar%3A%20%7B%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20baz%3A%20number%3B%0D%0A%20%20%20%20%20%20%20%20%7D%0D%0A%20%20%20%20%7D%0D%0A%7D%0D%0A%0D%0Atype%20F%20%3D%20%3C%0D%0A%20%20%20%20name%20extends%20keyof%20IExample%2C%0D%0A%20%20%20%20val%20extends%20keyof%20IExample%5Bname%5D%0D%0A%3E()%20%3D%3E%20IExample%5Bname%5D%5Bval%5D%5B'baz'%5D%3B

@ajafff
Copy link
Contributor

ajafff commented Feb 8, 2018

Likely related to #21368

@mhegazy mhegazy added the Bug A bug in TypeScript label Feb 8, 2018
@mhegazy mhegazy added this to the TypeScript 2.8 milestone Feb 8, 2018
@mhegazy
Copy link
Contributor

mhegazy commented Feb 8, 2018

@sandersn can you take a look.

@sandersn
Copy link
Member

sandersn commented Feb 8, 2018

The nut of the problem is that the base constraint of U extends keyof IExample[T] is string, even though we would like it be "bar". This means that IExample[T][U] doesn't have a constraint, and therefore can't be indexed by anything except keyof IExample[T][U], which "baz" is not.

I think it would be possible to fix this case by changing the rule for keyof types in computeBaseConstraint to return getIndexType(getBaseConstraint(t.type)) instead of just return stringType. I'll try it when I have time to see if it works.

@sandersn
Copy link
Member

sandersn commented Feb 8, 2018

Here's the change I made. It re-broke #15371, so I'm not sure it's the right change. I'll discuss with @ahejlsberg tomrrow.

                 if (t.flags & TypeFlags.Index) {
-                    return stringType;
+                    const baseType = getBaseConstraint((t as IndexType).type);
+                    const baseIndex = baseType ? getIndexType(baseType) : undefined;
+                    return baseIndex && baseIndex !== unknownType ? getBaseConstraint(baseIndex) : undefined;
                 }

@tenorok
Copy link
Author

tenorok commented Mar 28, 2018

@sandersn hello, maybe there is any news?

@wycats
Copy link

wycats commented Apr 14, 2018

This is a 2.8 upgrade blocker for me @DanielRosenwasser

@wycats
Copy link

wycats commented Apr 15, 2018

Actually, it's really weird! I'm getting type errors, but TS is getting the derived type right anyway!

@koloboid
Copy link

Is there any chance to get it fixed? I've got it working in my case, but only with strictNullChecks: false. We absolutely love strictNullChecks, so waiting for the fix)

@dkamyshov
Copy link

dkamyshov commented Oct 9, 2018

So I have this piece of code:

type D<T> = {
  [propertyName: string]: keyof T;
}

const f = <A, B, C extends D<B>>(
  a: A, b: B, c: C,
): (
  A & {
    [K in keyof C]: B[C[K]] // this line
  }
) => void 0 as any;

const result = f({
  abc: 1,
}, {
  def: true
}, {
  ghi: 'def'
});

In TS 3.0.3 result.ghi is correctly inferred as boolean and no errors are displayed.
In TS 3.1.1 result.ghi is (again) correctly inferred as boolean, but in highlighted line it says Type 'C[K]' cannot be used to index type 'B'.

I'd greatly appreciate any help.

@moretolike
Copy link

ERROR in node_modules/@amcharts/amcharts4/.internal/charts/types/XYChart.d.ts(258,33): error TS2344: Type 'this["_xAxisRendererType"]' does not satisfy the constraint 'AxisRenderer'.
Type 'AxisRendererX' is not assignable to type 'AxisRenderer'.
Types of property 'events' are incompatible.
Type 'SpriteEventDispatcher<AMEvent<AxisRendererX, IAxisRendererXEvents>>' is not assignable to type 'SpriteEventDispatcher<AMEvent<this["_xAxisRendererType"], this["_xAxisRendererType"]["_events"]>>'.
Type 'AMEvent<AxisRendererX, IAxisRendererXEvents>' is not assignable to type 'AMEvent<this["_xAxisRendererType"], this["_xAxisRendererType"]["_events"]>'.
node_modules/@amcharts/amcharts4/.internal/charts/types/XYChart.d.ts(264,33): error TS2344: Type 'this["_yAxisRendererType"]' does not satisfy the constraint 'AxisRenderer'.
Type 'AxisRendererY' is not assignable to type 'AxisRenderer'.
Types of property 'events' are incompatible.
Type 'SpriteEventDispatcher<AMEvent<AxisRendererY, IAxisRendererYEvents>>' is not assignable to type 'SpriteEventDispatcher<AMEvent<this["_yAxisRendererType"], this["_yAxisRendererType"]["_events"]>>'.
Type 'AMEvent<AxisRendererY, IAxisRendererYEvents>' is not assignable to type 'AMEvent<this["_yAxisRendererType"], this["_yAxisRendererType"]["_events"]>'.
node_modules/@amcharts/amcharts4/.internal/charts/types/XYChart.d.ts(503,31): error TS2344: Type 'this["_xAxisRendererType"]' does not satisfy the constraint 'AxisRenderer'.
node_modules/@amcharts/amcharts4/.internal/charts/types/XYChart.d.ts(513,31): error TS2344: Type 'this["_yAxisRendererType"]' does not satisfy the constraint 'AxisRenderer'.
node_modules/@amcharts/amcharts4/.internal/core/utils/Object.d.ts(60,117): error TS2536: Type 'Key' cannot be used to index type 'Object'.
node_modules/@amcharts/amcharts4/.internal/core/utils/Object.d.ts(67,109): error TS2536: Type 'Key' cannot be used to index type 'Object'.
node_modules/@amcharts/amcharts4/.internal/core/utils/Object.d.ts(76,116): error TS2536: Type 'Key' cannot be used to index type 'Object'.
node_modules/@amcharts/amcharts4/.internal/core/utils/Type.d.ts(25,32): error TS2304: Cannot find name 'Extract'.

Please.... Help Me...

@fabb
Copy link

fabb commented Jan 1, 2020

Maybe related:

function getValues<T, K extends readonly (keyof T)[]>(object: T, keys: K): { [P in keyof K]: T[K[P]] } {
    return keys.map(key => object[key]) as any
}

This shows the error: Type 'K[P]' cannot be used to index type 'T'.

@jfpolly
Copy link

jfpolly commented Jan 21, 2022

Maybe related:

// This does NOT compile

export type Nested = {
  nest: {
    foo: string[];
    bar: number[];
  };
};

export const test = <
  T extends keyof Nested,
  K extends keyof Nested[T],
  V extends Nested[T][K][number] //<-- Type 'number' cannot be used to index type 'Nested[T][K]'.
>(
  type: T,
  key: K,
  value: V
) => {
  console.log(type, key, value);
};

// desired interface:
test("nest", "foo", "value");
test("nest", "bar", 0);

Specifically, I want to get the type of the members of the nested array. Note, this works fine in the non-nested case, e.g.

// This compiles:

export type NonNested = {
  foo: string[];
  bar: number[];
};

export const test = <
  T extends keyof NonNested,
  V extends NonNested[T][number]
>(
  type: T,
  value: V
) => {
  console.log(type, value);
};

test("foo", "value");
test("bar", 0);

playground link: https://www.typescriptlang.org/play?#code/PTAEBUAsEsGdQCYHsCm8ByB5coDGSBbAB2gBsUBYAKGpQA8ikAnAF1BYE8iVR00WUCUAF5QAb2qhQAO34AucZKmgAZkiQLYLJtGkBzANoBdANxKpAIwCGTBdICuBCyibGzVKQF9336rQbMbPjSWuz8IqAAPEo49ALSCPAA1igcSCq8-IIANEoA0qBxKAnJqemZWoIG4Ea5HqAAaoV08YkVAgjVRgZ53Q5OLkagIJEAtKMQXDwA5P3OTNN4VtLSSGzOoPawguxIoLoI9OxToNN8lZ01PUbTAHTUAHwAFEqc3ArgdVIpHAp5X6AAG5WUj2FAKBrUACUIgeinqwVgSHIt1ISD0TzeKGyoB+OOBoJQUJ87moIEQaGgTB2ugETBUVlw4OoAi0TwARLItOycey1EgeaB2QSweziSz+ByuSxBezrExBQAGcU0VXklgweD4YhkND+RisY7cXhIaTnDoRCT1fmabS6QymJTyuyOeZuai+VX0A1BU2hVksdCm807UTReqxFrFNo-cpBs1ZBAAppFEomhMXLoGOaDR4vepYj4AkXgxrQ2HwqSI5EoVHozFTfEgsEqz0SrTxkMIDn82UlsXuAOdxMc+VKlVAA

@Andarist
Copy link
Contributor

Andarist commented Feb 24, 2023

An additional test case from @bmeck (TS playground):

const constRoutes = {
  users: {
    admin: {
      get: '/admin'
    }
  }
} as const
type ConstRoutes = typeof constRoutes
type ConstRouteSectionName = keyof ConstRoutes
type ConstRouteModelName<Section extends ConstRouteSectionName> = keyof ConstRoutes[Section]

function getForConstRoute<
  Section extends ConstRouteSectionName,
  Model extends ConstRouteModelName<Section>
>(section: Section, model: Model): string {
  // Cannot figure out that these are coming out of well known keys?
  return constRoutes[section][model].get
}
// ensure error on wrong param
getForConstRoute('users', 'admin2')

Although I think that perhaps this also suffers from lack of something like indexed access improvements for mapped types. It seems that TS could try to understand those kind of relationships better here - since those types are derived from other types.

@seansfkelley
Copy link

Unsure if this is related, but it seems so:

interface IndexedActions {
  a: {
    start: {
      foo: string;
    } 
  }
}

type DeriveInputType<N extends keyof IndexedActions, A extends keyof IndexedActions[N]> =
  IndexedActions[N][A] & { namespace: N; verb: A };

function doAction<N extends keyof IndexedActions, A extends keyof IndexedActions[N]>(action: DeriveInputType<N, A>) {
  const s: string = action.verb;  
  //                       ^^^^ `string | number | symbol`, not `'start'`
}

Playground Link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript
Projects
None yet
Development

No branches or pull requests