-
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
Suggestion: Structually assignable enum #22373
Comments
You could just use a string literal union instead of an enum? type JobStateAtServer = "enqueued" | "running" | "success" | "failed";
type JobStateAtClient = "enqueuing" | "enqueued" | "running" | "success" | "failed";
let s: JobStateAtServer = "enqueued";
let c: JobStateAtClient = s; // no error |
This is also largely equivalent to namespace E {
export const
foo = "foo",
bar = "bar";
}
type E = typeof E; |
Yes, it's true. But string literails can not get IDE input support and weak for typo like below. type JobStateAtServer = "enqueued" | "running" | "success" | "failed";
type JobStateAtClient = "enqueuing" | "enqueued" | "running" | "success" | "failed";
declare function enqueueJob(): Promise<JobStateAtServer>;
declare function getJobState(): Promise<JobStateAtServer>;
(async () => {
let jobState = "enqueuing";
jobState = await enqueueJob(); // OK
// string literal is not safe for typo
while (jobState != "failedwithtypo" && jobState != "success") {
jobState = await getJobState(); // OK
}
}); |
The only reason you don't get an error at the comparison there is because |
That's right. type JobStateAtServer = "enqueued" | "running" | "success" | "failed";
type JobStateAtClient = "enqueuing" | "enqueued" | "running" | "success" | "failed";
declare function enqueueJob(): Promise<JobStateAtServer>;
declare function getJobState(): Promise<JobStateAtServer>;
(async () => {
let jobState: JobStateAtClient = "enqueuing";
jobState = await enqueueJob(); // OK
// OK and I can get IDE input support.
while (jobState != "failed" && jobState != "success") {
jobState = await getJobState(); // OK
}
}); OK, i give up using enum... Thanks for reply and some solutions! |
After closing issue, I got some notice around this. First, I said "// OK and I can get IDE input support.". {
"editor.quickSuggestions": {
"other": true,
"comments": false,
"strings": true // <- here
}
} But this is so annoying because suggestion have top level keywords like Second, I can not rename string literals with IDE support. |
With the default vscode settings this should work: const x = "abc";
const y = "abc"; Right-click on either "abc" and rename, and both string literals should change. |
I confirmed it. But this is for replacing all "abc" string literals in project. Also, I've noticed "Find All Reference" is not valid for string literals. |
That's how structural types work -- they match based on the names of things, not based on the meaning. If structural types are dangerous but you want one enum to subtype another, you could do something like: enum JobStateAtClient{
enqueuing = "enqueuing",
enqueued = "enqueued",
running = "running",
success = "success",
failed = "failed",
}
type JobStateAtServer = JobStateAtClient.enqueuing | JobStateAtClient.running | JobStateAtClient.success | JobStateAtClient.failed; |
Yes, if I could. But JobStateAtServer is first defined(by api.json like swagger) and JobStateAtClient is extended with user states. So its definition is not right order. enum JobStateAtServer {
enqueued = "enqueued",
running = "running",
success = "success",
failed = "failed",
}
enum JobStateAtClient extends JobStateAtServer {
enqueuing = "enqueuing",
}
declare function enqueueJob(): Promise<JobStateAtServer>;
declare function getJobState(): Promise<JobStateAtServer>;
(async () => {
let jobState = JobStateAtClient.enqueuing;
jobState = await enqueueJob(); // this needs that enum is not nominal
while (jobState != JobStateAtClient.failed && jobState != JobStateAtClient.success) {
jobState = await getJobState(); // this needs that enum is not nominal
}
}); In this case,
So, it's cheaper to check if assignable. |
The syntax isn't as nice but you could emulate enum JobStateAtServer {
enqueued = "enqueued",
running = "running",
success = "success",
failed = "failed",
}
enum ClientSpecificJobState {
enqueuing = "enqueuing",
}
type JobStateAtClient = JobStateAtServer | ClientSpecificJobState;
// Can omit this namespace if you're willing to write `JobStateAtServer` or `ClientSpecificJobState` depending on which enum it comes from
namespace JobStateAtClient {
export const enqueued: JobStateAtClient = JobStateAtServer.enqueued;
export const running: JobStateAtClient = JobStateAtServer.running;
export const success: JobStateAtClient = JobStateAtServer.success;
export const failed: JobStateAtClient = JobStateAtServer.failed;
export const enqueuing: JobStateAtClient = ClientSpecificJobState.enqueuing;
} |
Thanks, it emulates the syntax correctly. |
Currently ts enum is nominal.
Even if two enums have the save content, they are not assignable each other.
This is good for the safety of coding at some point.
But please think below example.
Please assume
JobStateAtServer
,enqueueJob
andgetJobState
is generated by api.json(like swagger).JobStateAtClient
is defined at client user code and extended with the stateenqueuing
which means before enqueue.To solve this problem, I suggest following
structual
keyword for enum.The text was updated successfully, but these errors were encountered: