Hello all,
I am familiar with Haskell and JavaScript but new to TypeScript. If I have a string literal type type StrLitType = 'foo' | 'bar' | 'baz' and a value:string that is a run-time string from the user is it possible to write a generic parseStringLiteral<E>(value : string) : E function that will throw an error if the value is not valid with a list of possible values?
parseStringLiteral<StrLitType>('foo') => 'foo' : StrLitType
parseStringLiteral<StrLitType>('quux') => Error('Value "quux" is not one of "foo, bar, baz".)
Another engineer on my team is doing this in a one-off way with:
const envs = ['test', 'local', 'dev', 'staging', 'prod'] as const
const envsSet = new Set(envs)
export type NodeEnv = typeof envs[number]
const nodeEnv = env.NODE_ENV as NodeEnv
if (envsSet.has(nodeEnv)) {
return nodeEnv
}
throw new Error(`Invalid NODE_ENV: "${env.NODE_ENV ?? ''}". Should be something in ${envs.join(', ')}`)
But I am trying to abstract the pattern away and either can't get the incoming type correct, or can't get access to all the possible values. Current attempt:
function parseEnumValue<S extends Array<string>, E>(values : S, val : string) : E {
const valAsEnumUnsafe : E = val as E
if ( values.includes(val)) {
return valAsEnumUnsafe;
}
// todo could convert to set here, but shouldn't matter
throw new Error(`Invalid value "${val}" for string liter type of ${values.join(', ')}`)
}
But return parseEnumValue(envs, env.NODE_ENV || '') complains that "Argument of type 'readonly ["test", "local", "dev", "staging", "prod"]' is not assignable to parameter of type 'string[]'."