0

I recently encountered the following design-issue when trying to describe a Notification-format (and formats in general) with a Typescript interface.

Context: Notifications are exchanged (as JSONs) between the server (running javascript) and a client (that uses a different language).

I have tried to use an Interface like

interface Notification
{
    Text?: string,
    Title?: string,
    Priority?: number
}

But in my scenario I want the properties to be tied to string constants (which are exported from client sourcecode)

const kText = "Text";
const kTitle = "Title";
const kPriority = "Priority";

So if the format changes and we now have kText = "Message", the interface would automatically become

interface Notification
{
    Message?: string,
    Title?: string,
    Priority?: number
}

and ideally all instances of things like

notification.Text

should still stay valid - basically I'd like to have Text as an alias for the property, that is at the same time enforced to actually have kText as its name. Sort of like (not working at all, but maybe illustrates what I want):

type TextType = "Text"; // or "Message" if format changes later
type TitleType = "Title";
type PriorityType = "Priority";
interface Notification
{
    [Text : TextType]?: string,
    [Title : TitleType]?: string,
    [Priority : PriorityType]?: number
}

Is there any way to achieve something like this?

If not, what might be other good ways to implement this?

1 Answer 1

3

You can use the Record<TKey, TValue> type to define the interface:

type TextType = "Text"; // or "Message" if format changes later
type TitleType = "Title";
type PriorityType = "Priority";
type Notification = Partial<Record<typeof TextType | typeof TitleType, string>
        & Record<typeof PriorityType, number>>;

let notif: Notification;

let t = notif[TextType] // Will be string
let t2 = notif.Text // Also works 

The problem is there is no compiler way to enforce that access happens using the string constant, you could still access with .

Note

On typescript 2.7 and newer you can also do:

const TextType = "Text"; // or "Message" etc
const TitleType = "Title";
const PriorityType = "Priority";

interface Notification {
    [TextType]?: string
    [TitleType]?: string
    [PriorityType]?: number
}

But the same problem still applies of access

Sign up to request clarification or add additional context in comments.

3 Comments

Should I leave my answer up about computed property keys in interface? Or do you want to edit that into your answer if you think it's correct? Up to you
@jcalz That was my first instinct too, but my laptop has TS 2.6 and does not support computed interface properties, and I did't know if it made it in 2.7. 10x ;)
Okay, answer removed. Note for your 2.7 snippet to work there needs to be const Text = "Text" et al before the interface definition, so that Text resolves to a string-literal-typed constant.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.