TypeScript 4.1 introduced template literal types which, among other things, will convert enums to their string representations. So your goal can be accomplished simply as:
function doSomething(event: `${FMEvents}`) { }
doSomething("@firemodel/RECORD_CHANGED"); // okay
doSomething(FMEvents.RECORD_MOVED); // still okay
Playground link to code
Pre TS4.1 answer:
TypeScript doesn't make it easy to widen enum value types to the string or numeric literals from which they derive. (There is a complication that prevents using intersections to help with this) You can get fairly close to what you want using conditional types:
type Extractable<T, U> = T extends U ? any : never
type NotString<T> = string extends T ? never : any
function promoteStringToFMEvents<K
extends string & NotString<K> & Extractable<FMEvents, K>>(
k: K
): Extract<FMEvents, K> {
return k;
}
const fmAdded = promoteStringToFMEvents("@firemodel/RECORD_ADDED"); // FMEvents.RECORD_ADDED
const fmOops = promoteStringToFMEvents("@firemodel/RECORD_ADDLED"); // error
In the above code, Extractable<T, U>
returns any
if T
or any of its constituents is assignable to U
, and never
otherwise. And NotString<T>
returns any
is T
isn't string
or wider, and never
otherwise. By constraining K
in promoteStringToFMEvents()
to string & NotString<K> & Extractable<FMEvents, K>
, we are saying that the type parameter K
must be some string literal (or union of string literals) that some element (or union of elements) of FMEvents
can be assigned to.
So the function promoteStringToFMEvents()
will accept the string literals (or unions of string literals) you expect. The function also just returns the associated element of FMEvents
by assigning the input value to Extract<FMEvents, K>
, which pulls out just those pieces of FMEvents
which match K
.
So you can write your doSomething()
method such that it is generic in the type of K
above, and in the implementation of the method you can (if you need to) promote the string to an enum by assigning it to a variable of type Extract<FMEvents, K>
.
EDIT with explicit implementation of doSomething()
:
class Blomp {
public doSomething<K
extends string & NotString<K> & Extractable<FMEvents, K>>(k: K) {
// k is of some subtype of "@firemodel/RECORD_ADDED" |
// "@firemodel/RECORD_CHANGED" | "@firemodel/RECORD_MOVED" |
// "@firemodel/RECORD_REMOVED"
// if you need to interpret k as a subtype of FMEvents, you can:
const kAsFMEvent: Extract<FMEvents, K> = k;
// or even wider as just FMEvents
const fmEvent: FMEvents = kAsFMEvent;
// do what you want here
}
}
Hope that helps. Good luck!