Repository navigation
Using 'Key in keyof ArrayType' always returns an Array regardles of the value manipulation applied #40069
Description
Activity
This working as intended. Mapped arrays/tuples stay arrays/tuples as of TypeScript 3.1. See #26063.
KilianKilmister commented
on Aug 16, 2020 AuthorMore actionsI would argue that mapping its method & length keys to
nevermakes the resulting object non-homomorphic. It also causes generics that acceptobject-types act unpredictable and in no way type-safe.While it does work in accordance of the mentiond PR, this should then be considered a design oversight.
ForReference: Playground Link
type Values<T> = T[keyof T] type ReduceToUnique<T> = { [P in keyof T]: T[P] extends typeof Unique ? 'this was unique' : never } const Unique = Symbol("i'm unique") // 6 different Types, all of them extend `object` declare const objectType: object declare const anyType: any declare const notUniqueable: {someKey: 'someValue'} declare const isUniqueable: {someKey: typeof Unique} declare const notUniqueableArray: [1, 2, 3, 4, 5, 6, 7] declare const isUniqueableArray: [1, 2, 3, 4, 5, 6, typeof Unique] // a function that takes a generic `T extends object` declare function reduceToUniqueKV <T extends object>(someObject: T): ReduceToUnique<T> // the 6 Types processed by that function const objectT = reduceToUniqueKV(objectType) // ^-> object const anyT = reduceToUniqueKV(anyType) // ^-> ReduceToUnique<any> const notU = reduceToUniqueKV(notUniqueable) // ^-> ReduceToUnique<{ someKey: 'someValue'; }> const isU = reduceToUniqueKV(isUniqueable) // ^-> ReduceToUnique<{ someKey: typeof Unique; }> const notUArr = reduceToUniqueKV(notUniqueableArray) // ^-> [never, never, never, never, never, never, never] const isUArr= reduceToUniqueKV(isUniqueableArray) // ^-> [never, never, never, never, never, never, "this was unique"] // reducing the results to their property values declare const objectTValues: Values<typeof objectT> // ^-> never declare const anyTValues: Values<typeof anyT> // ^-> 'this was unique' declare const notUValues: Values<typeof notU> // ^-> never declare const isUValues: Values<typeof isU> // ^-> 'this was unique' declare const notUArrValues: Values<typeof notUArr> // ^-> 7 | (() => string) | (() => string) | (() => undefined) // | ((...items: never[]) => number) // | { (...items: ConcatArray<never>[]): never[]; // (...items: ConcatArray<never>[]): never[]; } // | ... 22 more ... | { ...; } declare const isUArrValues: Values<typeof isUArr> // ^-> 7 | "this was unique" | (() => string) | (() => string) // | (() => "this was unique" | undefined) // | ((...items: "this was unique"[]) => number) // | { (...items: ConcatArray<"this was unique">[]): "this was unique"[]; // (...items: ("this was unique" | ConcatArray<...>)[]): "this was unique"[]; } // | ... 22 more ... | { ...; }
A mapped type will not touch
lengthand methods keys for an array type, for exampletype ToNevers<T> = { [K in keyof T]: never }; type A = ToNevers<string[]>['length']; // still number, not never
The problem is you can't do
Arr[keyof Arr]to get its "values", you can useArr[number], so yourValuesshould betype Values<T> = T extends unknown[] ? T[number] : T[keyof T];
Reacted by Ajay Panwar- addedNeeds InvestigationThis issue needs a team member to investigate its status.This issue needs a team member to investigate its status.
on Sep 3, 2020 KilianKilmister commented
on Oct 26, 2020 AuthorMore actionsSimon Chan (@yume-chan) completly missed this response, so my apologies for the late reply
The problem is you can't do
Arr[keyof Arr]to get its "values", you can useArr[number], so yourValuesshould betype Values<T> = T extends unknown[] ? T[number] : T[keyof T];
Sure, this is easy when you expect an array, but if you just expect any generic object, this will really suprise you, see the example in my comment. Arrays have to be explicitly filtered out and handled differently (which i started doing after i ran into this).
But this also has become much less of an issue with some of the awedome new features #40336
TypeScript Version: 4.0.0-beta (same behaviour in version 3.5.1 v3.5.1-Play Ground)
Search Terms:
"key in keyof Array", "keyof Array"
Expected behavior:
using
key in ArrayTypehas the same behaviour as on any object (returning an array type only if the relevant properties are unchanged)Actual behavior:
using
key in ArrayTypeperforms actions on touple entries but the returned type allways includes all default Array propertiesExtention note:
Not the point of this issue and i know there are other issues around this topic. it's just a note,
but how about possibly adding tupple functionality for ArrayLike-, Map- and Set-types? They have all the basic qualities of a tupple, and it would be awesome to use them in a similar fashion (especially inference of
Map.prototype.get/setand[Symbol.iterator]inference of tupples)Related Issues:
#39726
#34780
type-fest/issues/119 (external library, but about this this behaviour and includes adHoc fix)
Code
infered types:
Output
Compiler Options
{ "compilerOptions": { "noImplicitAny": true, "strictNullChecks": true, "strictFunctionTypes": true, "strictBindCallApply": true, "noImplicitThis": true, "noImplicitReturns": true, "useDefineForClassFields": true, "alwaysStrict": true, "allowUnusedLabels": true, "noEmitHelpers": true, "noUnusedLocals": true, "noUnusedParameters": true, "preserveConstEnums": true, "experimentalDecorators": true, "emitDecoratorMetadata": true, "moduleResolution": 2, "target": "Latest", "module": "ESNext" } }Playground Link: Provided