microsoft / microsoft/TypeScript
Regression: Type T[K] as an array when sliced loses its type
Nobody has claimed this yet.
- 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:
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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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