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)