Home > Enterprise >  How can i type declare this in typescript? is it possible?
How can i type declare this in typescript? is it possible?

Time:11-14

I want to declare declare type HTMLElement && HTMLElement.prototype.style.position = 'absoltue' in typescript.

Is it possible?

type AbsoluteDisplay = {
  style: {
    position: 'absolute';
  };
};

const testFunc = (arg: ??) => {}; // I want arg type!

const truthyElem = document.createElement('div');
truthyElem.style.position = 'absolute';
testFunc(truthyElem); // Ok

const notAbsoluteHTMLElem = document.createElement('div');
testFunc(notAbsoluteHTMLElem); // No

const falsyElem: AbsoluteDisplay = {
  style: {
    position: 'absolute',
  },
};
testFunc(falsyElem); // No

CodePudding user response:

You can create simple helper for updating position property:

type Positions =
  | 'static'
  | 'relative'
  | 'fixed'
  | 'absolute'
  | 'sticky'


const updatePosition = <
  Elem extends HTMLElement,
  Position extends Positions
>(elem: Elem, position: Position) => ({
  ...elem,
  style: {
    ...elem.style,
    position
  }
})

Above function expects html element and one of allowed css positions. If you want to restrict it only to div - use Elem extends HTMLDivElement.

See full example:


type AbsoluteDisplay<E extends HTMLElement> =
  E & {
    style: {
      position: 'absolute';
    };
  };

const truthyElem = document.createElement('div')

type Positions =
  | 'static'
  | 'relative'
  | 'fixed'
  | 'absolute'
  | 'sticky'


const updatePosition = <
  Elem extends HTMLElement,
  Position extends Positions
>(elem: Elem, position: Position) => ({
  ...elem,
  style: {
    ...elem.style,
    position
  }
})

const absolute = updatePosition(truthyElem, 'absolute')
absolute.style.position // 'absolute'

const testFunc = <Elem extends HTMLElement>(arg: AbsoluteDisplay<Elem>) => { };

testFunc(absolute) // ok
testFunc(document.createElement('div')) // expected error

Or less generic version:

const updatePosition = <
  Elem extends HTMLElement,
  Position extends Positions
>(elem: Elem) => ({
  ...elem,
  style: {
    ...elem.style,
    position:'absolute'
  }
})

In this way testFunc is able to figure out that absolute constant has position:absolute.

Above way is prefered in typescript because it is much easier for compiler to work with immutable data structures.

However, if you want to mutate your variable without creating any extra variables, consider this example:


type AbsoluteDisplay<E extends HTMLElement> =
  E & {
    style: {
      position: 'absolute';
    };
  };

const testFunc = <Elem extends HTMLElement>(arg: AbsoluteDisplay<Elem>) => { };


function updatePosition<
  Elem extends HTMLElement,
  >(elem: Elem): asserts elem is AbsoluteDisplay<Elem> {
  elem.style.position = 'absolute'
}

const truthyElem = document.createElement('div')

// BEFORE MUTATION
truthyElem.style.position // string

updatePosition(truthyElem)

// AFTER MUTATION
truthyElem.style.position // absolute

testFunc(truthyElem) // ok
testFunc(document.createElement('div')) // expected error

You might have noticed, that truthyElem.style.position is infered as absolute after truthyElement was mutated by updatePosition

Here you can find documentation about assertion functions

CodePudding user response:

type AbsoluteDisplay = { style: { position: "absolute" } };

// [&] you can merge HTMLElement with your custom requirements
type HTMLElementPositionAbsolute = HTMLElement & AbsoluteDisplay;

// assume to get HTMLElement (easiest, not cleanest way to achieve things)
// I assume you like to return whether the type is correct...
const testFunc = (arg: HTMLElement): boolean => {
  // arg must match HTMLElement
  // (you might want to wrap this in a try/catch)
  if (!(arg instanceof HTMLElement)) return false;

  // style must be absolute
  return arg.style.position === "absolute";
};

const truthyElem = document.createElement("div");
truthyElem.style.position = "absolute";

// cast to type, you can write it also like this:
// testFunc(truthyElem as HTMLElementPositionAbsolute);
testFunc(<HTMLElementPositionAbsolute>truthyElem);

const notAbsoluteHTMLElem = document.createElement("div");
testFunc(notAbsoluteHTMLElem);

const falsyElem: AbsoluteDisplay = { style: { position: "absolute" } };
testFunc(<HTMLElement>falsyElem);

if this is on the right track we can go from here and do some refactoring (this can be resolved more elegant by using generic types)

  • Related