-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Consider adding symbolof
type operator, like keyof
but for unique symbol properties
#20721
Comments
I hope |
@yortus Indexes work, you're just accessing the type of a const SYM = Symbol('A Symbol');
const STR = 'A String';
interface Foo {
'lit': string;
[STR]: boolean;
[SYM]: number;
}
declare let foo: Foo;
let v1 = foo['lit']; // v1 is string
let v2 = foo[STR]; // v2 is boolean
let v3 = foo[SYM]; // v3 is number
// indexed access types
type T1 = Foo['lit']; // T1 = string
type T2 = Foo[typeof STR]; // T2 = boolean (note `typeof`)
type T3 = Foo[typeof SYM]; // T3 = number (note `typeof`) As for making |
Thanks @weswigham. So unique symbol values (and const strings) do not create same-named unit types. And this is by design as explained by @mhegazy in #20898 (comment). I think |
symbolof
type operator, like keyof
but for unique symbol properties
I updated the title and description to more clearly reflect what is being suggested here. |
Here's an example of where a
If we had a
Using overloads we can work around the issue somewhat, but we still have no type checking of symbol properties.
|
I've added significant justification for Those watching this issue may be interested in that, apologies for the dupe. |
Sorry, me again. More specifically the justification (which I think is fairly concrete) starts at this comment: #21983 (comment) |
I'm very in favor of at least a In the hypothetical case where you really need specifically string keys you'd presumably get a compile error somewhere from typescript when you try to pass or assign a Is it actually very common to require specifically strings though when you have a Edit: Also notable is the fact that
To me that pretty clearly shows that accurately reflecting how properties work in JavaScript is a goal of
It seems to me to be a fairly big hole in typescript to go through the trouble of adding the Admittedly the part about |
This was my view too but it’s a breaking change for TS 2.x unfortunately.
See here for the discussion:
#21983
…On Fri, 30 Mar 2018 at 01:08, Kevin Donnelly ***@***.***> wrote:
I'm very in favor of at least a symbolof operator, although personally
I'm even more in favor of just having keyof return symbols as well.
Having a separate operator in cases when you want to operate on both would
be pretty cumbersome and I imagine for that reason a lot of types would
continue to be written only with keyof even if at runtime they'd have no
problem handling symbols as well.
In the hypothetical case where you really need specifically string keys
you'd presumably get a compile error somewhere from typescript when you try
to pass or assign a string | symbol to a spot that requires a string. And
then you could rewrite keyof T to be string & keyof T to fix the error.
Is it actually very common to require specifically strings though when you
have a keyof T type? The only specific case I saw mentioned was the
return type of Object.keys, but that doesn't actually return a keyof T in
typescript. It just returns string[] because typescript can't be sure
that there won't be extra fields on T at runtime that it doesn't know
about.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#20721 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AhZXAKdgDX1VU-T4Dt6zpUwofGABfEaJks5tjXd1gaJpZM4RDcrW>
.
|
Uh so fun fact I just learned: it turns out const sym = Symbol();
const obj = { num: 0, str: 's', [sym]: sym };
function set <T extends object, K extends keyof T> (obj: T, key: K, value: T[K]): T[K] {
return obj[key] = value;
}
const val = set(obj, 'str', '');
// string
const valB = set(obj, 'num', '');
// Expect type error
// Argument of type '""' is not assignable to parameter of type 'number'.
const valC = set(obj, sym, sym);
// ~~Unexpected type error~~
// ~~Argument of type 'unique symbol' is not assignable to parameter of type '"str" | "num"'.~~
// Not anymore in TS 2.8
type KeyofObj = keyof typeof obj
// typeof sym | 'str' | 'num' in 2.8
type Values<T> = T[keyof T]
type ValuesOfObj = Values<typeof obj>
// string | number | symbol in 2.8 I just confirmed that that didn't previously work in 2.7. As far as I can tell the only thing we are actually missing now is the ability to use symbols in mapped types. If it was really a breaking change to make |
That is weird. I wonder if this change to The change doesn't seem in line with @weswigham's comment above, and as you point out breaking changes would normally be documented. |
@weswigham It looks like you ended up changing |
You shouldn't rely on |
Forgive me for being blunt, but I don't see how you can say changing it would likely break a lot when you already did change it to no longer always only be a subtype of string and it didn't break enough for anyone to even notice. const sym = Symbol()
const obj = { num: 0, str: 's', [sym]: true }
declare const objKeys: keyof typeof obj
const strObjKeys: string = objKeys
// error unique symbol is not assignable to string in 2.8 So If you do have to change it back though and add another operator instead, I'd prefer Edit: Clarified that I was wrong when I thought any potential breaking changes would've already been happening in 2.8. |
That's because declare function log(x: string): void;
function f<T>(x: T, k: keyof T) {
log(k);
} still works (not may people have reason to call |
Fair enough I wasn't thinking of that. I still question how common that use case(trying to assign If a function getPropValue<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key]
} Which would be much better typed with I think that we'd end up in an unfortunate situation where accurate types should normally be using Not the end of the world but it'd be nice to avoid. Especially since the workaround for the (still hypothetical to me) cases where it'd be a breaking change seems to be as simple as: declare function log(x: string): void;
function f<T>(x: T, k: string & keyof T) {
log(k);
} |
@kpdonn I think you make a valid point there. There's already a precedent for that happening with I like your suggestion to have a single general operator ( |
With #23592 |
I was worried about However with the introduction of condition types and |
EDIT: The example code below mostly does work as per #20721 (comment). The only part remaining unsolved is that there is no equivelent of
keyof
for unique symbols. The suggestion is to add asymbolof
type operator.TypeScript Version: 2.7.0-dev.20171215
Thanks to #15473, unique symbols and string consts can be used as keys in types. But they don't work with indexed access types or index type queries (#11929), as demonstated below.
Code
Expected behavior:
No errors.
T2
isboolean
andT3
is number.keyof Foo
is'A string' | 'lit' | SYM
Actual behavior:
Errors for
T2
andT3
, andkeyof Foo
doesn't includeSYM
.I know there are several overlapping features in play here, but would like to know if all of the actual behaviour above is by design. E.g., maybe not having symbols show up in
keyof
queries is by design, but what about the fact that we can't query types withFoo[STR]
orFoo[SYM]
, even though the compiler knows the types and the syntax is consistent withFoo['lit']
?The text was updated successfully, but these errors were encountered: