I was trying to typehint the gridTemplateColumns
css property, and in a dependency of React called csstype I found the following property definition:
gridTemplateColumns?: Property.GridTemplateColumns<TLength> | undefined;
This is a property of the following interface, where TLength
and TTime
are declared.
export interface StandardLonghandProperties<TLength = (string & {}) | 0, TTime = string & {}> {
I don't understand what string & {}
is trying to accomplish. How is it different than just a regular string
?
CodePudding user response:
Because the so-called empty object type {}
only excludes null
and undefined
(see How to undestand relations between types any, unknown, {} and between them and other types?), the intersection string & {}
is equivalent to string
in terms of what values it accepts. So for most purposes there is no difference between string
and string & {}
and thus no reason to use the more complicated type.
But even though string & {}
might be considered the same as string
, the compiler does not eagerly reduce the former to the latter, the way it does for, say, string & unknown
(where the unknown
type is absorbed by the intersection with string
), or the way it does for union types like string | "x"
(where the string literal type "x"
is absorbed by the union with string
).
So one use of string & {}
is to prevent some of these eager reductions for IntelliSense autosuggest/autocomplete and documentation purposes. The type (string & {}) | "x"
allows the same values as string
, but still has enough "memory" of the "x"
type for it to be suggested by an IntelliSense-enabled IDE. This technique is mentioned in microsoft/TypeScript#29729 as a workaround for the lack of native support for autocompletion of string literal unions with string
.
Still, in the case of (string & {}) | 0
and string & {}
as shown in the definition for the StandardLonghandProperties
interface in frenic/csstype, it seems unmotivated. There's no difference in the IntelliSense you'd be prompted with for (string & {}) | 0
and just string | 0
(since 0
is a numeric literal it doesn't get absorbed in the union), and there's definitely no difference in the IntelliSense prompting for string & {}
and just string
, since there are no literals to suggest at all.
So, why is it like that? If you look at the frenic/csstype#73 issue, it seems that there are a bunch of places that do use a union of string literals with string
, where IntelliSense suffered. Apparently frenic/csstype has its its own build system to generate the TypeScript declaration file. So the fix in frenic/csstype#74 was to just change the generation code so that instead of just string
and number
types, it would generate (string & {})
and (number & {})
.
That means there really isn't much of a reason for string & {}
in the code you're looking at. If the maintainers of frenic/csstype wanted to, they could change the code so that string
and number
are left alone in places where they are not part of a union with literals of the same base type. That is, they could make sure that & {}
only shows up in places where it improves IntelliSense suggestions.
But that would take more work. And while such a change might reduce the sort of confusion that prompted this question, it's not like the current code has a bug. Instead, it's a (mostly) harmless side-effect.