microsoft / microsoft/TypeScript

Regression: Type T[K] as an array when sliced loses its type

Open
#31,549 3 comments 3 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Bug Domain: Indexed Access Types Needs Human Review
Dominant language
Go
Stars
111k
Forks
14.3k
Avg merge
2d 4h
Merged PRs (30d)
132

Description

TypeScript Version: 3.4.5

Search Terms:
array property generics
Code

export function GetArrayProp<U, K extends string, T extends Record<K, U[]>>(
  state: T,
  prop: K,
) {
  //good
  let x1: T[K] = state[prop];
  //good
  let x2: U[] = state[prop];
  //good
  let x3: U[] = state[prop].slice(0);
  //error
  let x4: T[K] = state[prop].slice(0);
}

While you can argue that U[] isn't necessarily assignable to type T[K]. Look at the following code.
It's hard to see how TypeFromArray<T[K]>[]'s type should be any different than T[K]

export type TypeFromArray<T> = T extends (infer R)[] ? R : never;

export function GetArrayProp<K extends string, T extends Record<K, Array<TypeFromArray<T[K]>>>>(
  obj: T,
  prop: K,
) {
  //good
  let x1: T[K] = obj[prop];
  //good
  let x2 = obj[prop].slice(0);
  //error Type 'TypeFromArray<T[K]>[]' is not assignable to type 'T[K]'.
  let x3: T[K] = obj[prop].slice(0);
}

Expected behavior:

No errors.
T[K] and U[] should be assignable to each other.
state[prop].slice(0) should be of the same type as state[prop]
TypeFromArray<T[K]>[]'s type should be not be any different than T[K]

This code works in version 3.2.4 but stopped working in 3.3.1

Actual behavior:

Error Type 'U[]' is not assignable to type 'T[K]'.
Error Type 'TypeFromArray<T[K]>[]' is not assignable to type 'T[K]'

Playground Link:

Playground Link

My main goal is to be able to pluck array properties and modify the arrays without losing the type T[K]
The code above is simplified to show how TS is behaving.
See my use case below.

It's hard to see how TypeFromArray<T[K]>[]'s type should be any different than T[K]
The code below used to work when using 3.2.4


import { EntityState, EntityAdapter } from '@ngrx/entity';
import { Action } from '@ngrx/store';

export type TypeFromArray<T> = T extends (infer R)[] ? R : never;

type Selector<T> = (s: T) => boolean;


function ModifyEntityFromState<T>(entityId: number, state: EntityState<T>, callBack:  (acnt: T) => void): T {
  const t = state.entities[entityId];
  const acnt = JSON.parse(JSON.stringify(t)) as T;
  callBack(acnt);
  return acnt;
}


export function ModifyPropertyFromState<T, K extends keyof T>(
  entityId: number,
  state: EntityState<T>,
  prop: K,
  callBack: (a: T[K]) => T[K],
): T {
  return ModifyEntityFromState( entityId, state, (acnt) => {
    acnt[prop] = callBack( acnt[prop]  );
  });
}


export function EditArrayFromState<
T extends Record<K, Array<TypeFromArray<T[K]>>>,
K extends keyof T,
U extends TypeFromArray<T[K]>
>(
  entityId: number, state: EntityState<T>, prop: K,
  selector: Selector<U>,
  modified: U
): T {
  return ModifyPropertyFromState( entityId, state, prop, (a) => {
//Argument of type '(a: T[K]) => TypeFromArray<T[K]>[]' is not assignable to parameter of type '(a: T[K]) => T[K]'.
//  Type 'TypeFromArray<T[K]>[]' is not assignable to type 'T[K]'
    const idx = a.findIndex(selector);
    return  [
      ...a.slice(0, idx),
      ...a.slice(idx + 1),
      modified
    ];
  });
}



Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the issue using the supplied TypeScript snippets and Playground link, comparing behavior in 3.2.4, 3.3.1, and 3.4.5. Trace the generic indexed-access and array-slice type checking, then add a regression test showing that the sliced value remains assignable to T[K] and verify the test suite.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.