Frailty, thy name is TypeScript

Frailty, thy name is TypeScript

I have a growing love-hate relationship with TypeScript. I'm in favor of strongly-typed languages, they can save you a lot of grief, and they (typically) make the code easier to read for the next guy. Even if the "next guy" is just you a year later.

I needed a type that says "give me all the valid CSS properties you can use in JavaScript to set a style." And sure, there's a library out there that will give it to you, but I'm avoiding dependencies for ... easy things.

So I did the easy thing and defined CSSAttributes for the function below with the type above it: HTMLElement

type CSSAttributes = keyof CSSStyleDeclaration;

// OH NO! An error! 
// TS2540: Cannot assign to 'length' because it is a read-only property
function reticulate(
    elm: HTMLElement, 
    attrib: CSSAttributes, 
    value: string
): void {
    elm.style[attrib] = value;
}

So there's some read only properties in there. I already know, at this point, that there's a utility type of Omit<Type, Keys> so I'll use that to remove the read only props.

type toOmit =
      'length'
    | 'parentRule'
    | 'item'
    | 'removeProperty'
    | 'setProperty'
    | 'getPropertyPriority'
    | 'getPropertyValue';

type CSSAttributes = keyof Omit<CSSStyleDeclaration, toOmit>;

// OH NO! Another error! 
// TS2322: Type 'string' is not assignable to 
//         type 'string & (() => IterableIterator<string>)'
elm.style[attrib] = value;

Well that makes sense. Some light digging shows that there's a typeof Symbol.iterator in there I need to exclude, adding it to toOmit and it's 100% fixed – I now see in my IDE the type of 'number' is allowed. Yikes, lets remove that too. Finally, a very simple type definition:

type toOmit =
    'transform'
    | 'getPropertyPriority'
    | 'getPropertyValue'
    | 'item'
    | 'length'
    | 'parentRule'
    | 'removeProperty'
    | 'setProperty'
    | typeof Symbol.iterator
    | number

type CSSAttributes = keyof Omit<CSSStyleDeclaration, toOmit>;

How could this possibly be more simple? :D Full disclosure, it would have been easier to do this:

// at the command line…
> npm i csstype
// in my code…
import type * as CSS from 'csstype';
type CSSAttributes = keyof CSS.Properties;

It's got some other nice parts to it, which is why I ditched my definition and used the csstype library.