-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Support number and symbol named properties with keyof and mapped types #23592
Conversation
# Conflicts: # src/compiler/checker.ts # src/server/utilities.ts
@lkgarrison, that package has not yet been updated to use |
Any recommendations for how library developers should proceed with this breaking change? For example, I maintain a project available via npm, written in typescript, and distribute its type definitions file (which includes generic types and method signatures involving I could modify the source code to compile in TypeScript 2.9 by replacing all instances of Are there any tricks I can use to define my generic classes/methods involving Is there some way to conditionally define a generic type differently depending on TypeScript versions. I basically need (example inspired by C compiler directives): #if TYPESCRIPT_VERSION >= 2.9
type ExtractStringKeysType<T> = Extract<keyof T, string>;
#else
type ExtractStringKeysType<T> = keyof T;
#endif |
|
@UselessPickles I recommend using |
@weswigham Thanks, I'll give that a try and report back to confirm that it works (at least for TS versions 2.3-2.9). If this is the solution, I think it should be added as a suggestion to the breaking changes documentation. @ahejlsberg That is a very useful tip (maybe should be added to the breaking changes documentation?), but actually seems to be opposite of what I was looking for (a way to consistently get only the string-like key types of some type T in all TS versions). Some more context... The project I'm trying to future-proof for TS 2.9: https://github.com/UselessPickles/ts-enum-util At the core of the problem is that I use this type as a constraint to support any "enum-like" types (objects with export type EnumLike<V extends number | string, K extends string> = {
[P in K]: V
}; Then the main generic class that uses that type constraint: export class EnumWrapper<
V extends number | string = number | string,
T extends EnumLike<V, keyof T> = any
> Which allows me to have a very strictly typed utility wrapper for any enum that knows the exact type of values in the enum ( I think changing my constraint in |
@weswigham Unfortunately, the Prior to TS 2.9, the interface Test {
A: number;
B: string;
}
type StringKeys<T> = keyof T & string;
type StringKeyValues<T> = T[StringKeys<T>];
// expect: "A" | "B"
// got: ("A" & string) | ("B" & string)
type Keys = StringKeys<Test>;
// expect: number | string
// got: any
type Values = StringKeyValues<Test>; |
I am trying to set an enum as the key of an interface in 2.7.2: so I can have objects as {'a':1} Am I doing something wrong? |
@seaBubble The type of the index signature key can only be number or string. You have to use a mapped type to create an interface whose keys are the values of a string enum: enum TestEnum {
A = 'a',
B = 'b'
}
type TestCounter = { [P in TestEnum]: number };
const testCounter: TestCounter = {
[TestEnum.A]: 0,
[TestEnum.B]: 0
}; This can be further generalized with generics, and can also work with string literal unions. Check out my generic strictly typed string visitor/mapper utility for more advanced generic examples: https://github.com/UselessPickles/ts-string-visitor In particular, look at the StringMapperCore interface in src/StringMapper.ts. |
@ahejlsberg will there be support for enum E = { A, B }
declare const keyedByEnum: { [ P in E ]: any; }
for (const key in keyedByEnum) {
// key needs to be of type E
} and const keys = Object.keys(keyedByEnum); // keys need to be E[] ? |
In both cases the key is a string. |
bummer, you are right
…On Sat, May 26, 2018, 21:49 Mohamed Hegazy ***@***.***> wrote:
In both cases the key is a string.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#23592 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AA5PzcbfZaUivSVSVGKnjp_CXnKCbHS6ks5t2gYdgaJpZM4TeMpQ>
.
|
This PR adds support for
number
andsymbol
named properties in index types and mapped types. Previously, thekeyof
operator and mapped types only supportedstring
named properties. Since this is technically a breaking change, the PR includes a--keyofStringsOnly
compiler option that can be used to disable the new behavior in projects in which it causes errors (we expect this to be relatively rare).Changes include:
keyof T
for some typeT
is a subtype ofstring | number | symbol
. This is a breaking change askeyof T
is no longer assignable to juststring
.{ [P in K]: XXX }
permits anyK
assignable tostring | number | symbol
.for...in
statement for an object of a generic typeT
, the inferred type of the iteration variable was previouslykeyof T
but is nowExtract<keyof T, string>
. (In other words, the subset ofkeyof T
that includes only string-like values.)Given an object type
X
,keyof X
is resolved as follows:X
contains a string index signature,keyof X
is a union ofstring
,number
, and the literal types representing symbol-like properties, otherwiseX
contains a numeric index signature,keyof X
is a union ofnumber
and the literal types representing string-like and symbol-like properties, otherwisekeyof X
is a union of the literal types representing string-like, number-like, and symbol-like properties.The string-like properties of an object type are those declared using an identifier, a string literal, or a computed property name of a string literal type. The number-like properties of an object type are those declared using a numeric literal or computed property name of a numeric literal type. The symbol-like properties of an object type are those declared using a computed property name of a unique symbol type (see #15473).
In a mapped type
{ [P in K]: XXX }
, each string literal type inK
introduces a property with a string name, each numeric literal type inK
introduces a property with a numeric name, and each unique symbol type inK
introduces a property with a unique symbol name. Furthermore, ifK
includes typestring
, a string index signature is introduced, and ifK
includes typenumber
, a numeric index signature is introduced.Some examples:
Since
keyof
now reflects the presence of a numeric index signature by including typenumber
in the key type, mapped types such asPartial<T>
andReadonly<T>
work correctly when applied to object types with numeric index signatures:Furthermore, with the
keyof
operator's support fornumber
andsymbol
named keys, it is now possible to abstract over access to properties of objects that are indexed by numeric literals (such as numeric enum types) and unique symbols.Fixes #13715.
Fixes #14359.
Fixes #20721.
Fixes #21983.
Fixes #22105.