I'm using TypeScript to work with animation data, and I'm trying to create a Keyframe
interface (or some other way to tell TypeScript about the shape of a keyframe).
A Keyframe
might be one of three different configurations:
- those animating only transform properties
- those animating only non-transform properties
- those mixing both.
Keyframes might look like this:
const transformKeyframe = {
'easing': { 'translateX': 'easeInOutQuad' },
'transform': { 'translateX': '10px' }
}
const propertyKeyframe = {
'easing': { 'opacity': 'linear' },
'opacity': '0.5'
}
const mixedKeyframe = {
'easing': {
'translateX': 'easeInOutQuad',
'translateY': 'easeInOutQuad',
'opacity:': 'linear',
'fill:': 'linear'
},
'transform': {
'translateX': '10px',
'translateY': '10px',
},
'opacity:': '0.5',
'fill:': 'red'
}
const finalKeyframe = {
{
'easing': { },
'opacity': '0.5'
}
}
A Keyframe:
always has an
easing
object, which will either be empty, or contain[key: string]: string
entries. These keys will always be one of the ~200 animatable CSS properties, in camelCase.sometimes has a
transform
object, containing[key: string]: string
entries. These keys will always be one of the 18 CSS transform properties, in camelCase.sometimes has
[property]
key:value pairs. These keys will always be one of the ~200 animatable CSS properties (excluding the 18 transform properties), and will never be the strings'easing'
or'transform'
always has either the
transform
object, or at least one[property]
key:value pair.
What I have been trying is something like this:
interface KeyFrame {
easing: Record<string, string>;
transform?: Record<string, string>;
[property: string]: string;
}
One apparent problem is that because 'easing'
and 'transform'
fit the pattern of being string keys, they are interpreted as an instance of [property: string]: string;
, which causes an error because their values are not of type string
. At least, I think that's what's happening..?
I'm new to Typescript, and may be missing something obvious, but I can't find the answer to this in the handbook.
CodePudding user response:
As far as I know, this isn't really possible with index signatures, by defining [property: string]: string
, TS is really expecting all values to match. The workaround is a union [property: string]: string | Record<string, string>
, but this is very often not what is desired.
TypeScript comes with a built-in type called CSSStyleDeclaration
which will have all the keys you want, I think...
We'll intersect all the possible keys, with our own easing
and transform
values. I couldn't find a similar built-in list for transform values, so we'll keep that Record<string, string>
type KeyFrame = Partial<CSSStyleDeclaration> & {
easing: Partial<CSSStyleDeclaration>
transform?: Record<string, string>
}
We have to use a type alias, because CSSStyleDeclaration
already has a transform key/value configured, but it's set to string
. So we can overwrite using a type alias and intersection