Home > Net >  Cannot have multiple properties when having one property's name passed via generic
Cannot have multiple properties when having one property's name passed via generic

Time:12-18

I have this example which works well:

export type TimeStamped<TContent, TContentName extends string> = {
    [P in TContentName]: TContent
}

type Food = 'apple' | 'banana' | 'pear'
type TimeStampedFood = TimeStamped<Food, 'food'>

type Animal = 'cat' | 'dog' | 'horse'
type TimeStampedAnimal = TimeStamped<Animal, 'animal'>

const banana: TimeStampedFood = {food: 'banana'}
const cat: TimeStampedAnimal = {animal: 'cat'}

console.log(banana, cat)

It's a bit pointless though. So let's keep track of the creation and expiry dates of these objects, like so:

export type TimeStamped<TContent, TContentName extends string> = {
    [P in TContentName]: TContent,
    createdAt: DateTimeString,
    expiresAt?: DateTimeString
}

type Food = 'apple' | 'banana' | 'pear'
type TimeStampedFood = TimeStamped<Food, 'food'>

type Animal = 'cat' | 'dog' | 'horse'
type TimeStampedAnimal = TimeStamped<Animal, 'animal'>

const banana: TimeStampedFood = {
    food: 'banana',
    createdAt: '2020-01-01T00:00:00Z',
    expiresAt: '2020-01-01T01:00:00Z'
}
const cat: TimeStampedAnimal = {
    animal: 'cat',
    createdAt: '2016-01-01T00:00:00Z',
    expiresAt: '2023-01-01T01:00:00Z'
}

console.log(`The ${banana.food} expires at ${banana.expiresAt}`)

This wont work so well because I get an error on the type declaration: type definition of TimeStamped

Expecting newline or semicolon
Unnecessary label 'createdAt' 
TS2304: Cannot find name 'expiresAt'
TS1128: Declaration or statement expected.

My goal is to have an object with createdAt and optionally expiresAt properties defined but the property for the content should allow any name. Is there a way of doing what I'm trying to do?.

CodePudding user response:

You can't do it that way, see e.g. How to combine known interface properties with a custom index signature?

Docs: https://www.typescriptlang.org/docs/handbook/2/objects.html#index-signatures

How about one of these options:

export interface TimeStamped {
    createdAt: DateTimeString;
    expiresAt: DateTimeString;
}

type Food = 'apple' | 'banana' | 'pear'
type TimeStampedFood = { food: Food; } & TimeStamped;
// https://www.typescriptlang.org/docs/handbook/2/objects.html#intersection-types

type Animal = 'cat' | 'dog' | 'horse'
interface TimeStampedAnimal extends TimeStamped {
     animal: Animal; 
}
// https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#interfaces

const banana: TimeStampedFood = {
    food: 'banana',
    createdAt: '2020-01-01T00:00:00Z',
    expiresAt: '2020-01-01T01:00:00Z'
}
const cat: TimeStampedAnimal = {
    animal: 'cat',
    createdAt: '2016-01-01T00:00:00Z',
    expiresAt: '2023-01-01T01:00:00Z'
}
  • Related