Reputation: 6007
I have this type declaration:
declare const csvSeparator: unique symbol;
export declare type CSVSeparator = ';' | ',' | ' ' | '|' | '.' | '\t'
& { readonly [csvSeparator]: 'CSVSeparator' };
Then I try to define this structure:
const predefinedColumnSeparators: ColumnSeparatorInterface[] = [
{name: 'Semicolon', character: ';'},
{name: 'Comma', character: ','},
{name: 'Space', character: ' '},
{name: 'Pipe', character: '|'},
{name: 'Colon', character: '.'},
{name: 'Tabulator', character: '\t'},
];
But in here I get an error Type '"\t"' is not assignable to type 'CSVSeparator'
.
How can I declare my custom type correctly? Or is there any better way to define acceptable values?
Upvotes: 1
Views: 556
Reputation: 74710
You have made '\t'
in CSVSeparator
a nominal string type (also called branded primitive). The error in the assignment occurs, because a regular '\t'
string cannot be assigned to this nominal '\t'
anymore. Also the nominal typing (the intersection part) is only applied to '\t'
:
type CSVSeparator = ... | ... | ('\t' & { readonly [csvSeparator]: 'CSVSeparator' })`
// read like this: ^ ^
I think, you have two options here: 1. use regular strings (the simple one) or 2. branded strings (stronger types).
1. Simple string (Code)
export type CSVSeparator = ';' | ',' | ' ' | '|' | '.' | '\t'
const predefinedColumnSeparators: { name: string; character: CSVSeparator }[] = [
{name: 'Semicolon', character: ';'},
...
{name: 'Tabulator', character: '\t'},
];
2. Branded string (Code)
declare const csvSeparator: unique symbol;
// nominal string type
type Branded<T extends string> = T & { readonly [csvSeparator]: 'CSVSeparator' }
// helper function: create branded string by applying a type assertion to it
function brand<T extends string>(s: T) {
return s as Branded<T>
}
export type CSVSeparator = Branded<';'> | Branded<','> | Branded<' '> | Branded<'|'>
| Branded<'.'> | Branded<'\t'>
const predefinedColumnSeparators: { name: string; character: CSVSeparator }[] = [
{ name: 'Semicolon', character: brand(';') },
{ name: 'Semicolon', character: ';' }, // error, no regular string assignable
...
{ name: 'Tabulator', character: brand('\t') }
];
The branding is just a type declaration at compile-time, you can use it as regular string
:
const regularString: string = brand(";")
Upvotes: 3
Reputation: 11999
I'm not sure how you want to use CSVSeparator
, but you may be able to use an enum
instead
enum CSVSeparator {
Semicolon = ";",
Comma = ",",
Space = " ",
Pipe = "|",
Colon = ".",
Tabulator = "\t"
}
Upvotes: 3