Reputation: 8455
I've poured over the TS docs, but can't seem to find an answer as to why the following is illegal.
I'm able to define a union type with strings, yet if I create the same union type with references to object properties, which ultimately references the same string values, the compiler isn't happy.
Demo via typescript playground
// Success
export type SortOrderWithStrings = 'asc' | 'desc';
export const SortOrderStatic = {
ASC: 'asc',
DESC: 'desc',
}
// Fail
export type SortOrderWithObject = SortOrderStatic.ASC | SortOrderStatic.DESC;
The errors are as follows. Why is this a namespace issue?
Cannot find namespace 'SortOrderStatic'
Exported type alias 'SortOrderReference' has or is using private name 'SortOrderType'
Ultimately, it'd be nice to have a Column
interface the defines the supported sort order types, while then instancing a Column
with SortOrderStatic
to avoid magic strings, but per the above, this doesn't seem possible.
interface Column {
label: string;
defaultSortOrder: SortOrderWithObject;
}
const MyColumn: Column = {
label: 'My Column',
defaultSortOrder: SortOrderStatic.ASC
}
If I define the interface as follows (with SortOrderWithStrings
), and then attempt to use SortOrderStatic
, the compiler once again isn't happy.
interface Column {
label: string;
defaultSortOrder: SortOrderWithStrings;
}
const MyColumn: Column = {
label: 'My Column',
defaultSortOrder: SortOrderStatic.ASC
}
Fails with:
Type 'string' is not assignable to type 'SortOrderWithStrings'.
It's clear I'm missing something fundamental here, and I appreciate any insight.
Upvotes: 0
Views: 174
Reputation: 22030
It's clear I'm missing something fundamental here
Yes, you are confusing compile-time with runtime: unless the compile-time type is a literal string, the compiler cannot guarantee that the runtime access of an object property is the string you think it is. Your object property is type string
, which means at runtime it could be any string.
The string literal is immutable (and so statically verifiable), the object property access not so much.
In your particular case I would just use an enum:
enum SortOrder {
ASC = 'ASC',
DESC = 'DESC',
}
export { SortOrder }
import { SortOrder } from 'something';
SortOrder.ASC; // ASC as a string
This will maintain type-safety, you can't pass any ol' string to something that expects a SortOrder
: (Playground)
Upvotes: 1