3
var attributeStr = 'scaleX'

interface CSSTransformsAttr{
   scaleX: number,
   scaleY: number,
   skewX: number,
   skewY: number,
   translateX: number,
   translateY: number
}

How to check if the value of attributeStr is equal to a key in CSSTransformsAttr ?

Why do i ask this?

I want to extends the CSSStyleDeclaration with CSSTransformsAttr. If k is key of CSSTransformsAttr which is 'scaleX' | 'scaleY' | 'skewX' | 'skewY' | 'translateX' | 'translateY', i want to do something different than line (1). See below.

function setStyleAttributes(attrs: { [ k in keyof ( CSSStyleDeclaration & CSSTransformsAttr )]?: any }): void {
    if (attrs !== undefined) {
            Object.keys(attrs).forEach((key: string) => {
                /// how to check if 'key' is from the CSSTransfomsAttr not CSSStyleDeclaration ?
                /// ...
            this.htmlElement.style[key]=  attrs[key]; // (1) if key in keyof CSSStyleDeclaration
        });
    }
}

3 Answers3

2

Simply checking keyof is only going to work at compile time, not runtime. You need to store all the keys in an array and check if your value exists in it. This can be done manually:

interface CSSMatrixTransforms{
   scaleX: number,
   scaleY: number,
   skewX: number,
   skewY: number,
   translateX: number,
   translateY: number
};
type CSSMatrixTransformsKey = keyof CSSMatrixTransforms;
const matrixKeys: CSSMatrixTransformsKey[] = [
   'scaleX',
   'scaleY',
   'skewX',
   'skewY',
   'translateX',
   'translateY'
];

Alternatively, you can create a "dummy" object that implements your interface and then call Object.keys on it.

interface CSSMatrixTransforms{
   scaleX: number,
   scaleY: number,
   skewX: number,
   skewY: number,
   translateX: number,
   translateY: number
}
type CSSMatrixTransformsKey = keyof CSSMatrixTransforms;
const dummy: CSSMatrixTransforms = {
   scaleX: 0,
   scaleY: 0,
   skewX: 0,
   skewY: 0,
   translateX: 0,
   translateY: 0
}
const matrixKeys: CSSMatrixTransformsKey[] = Object.keys(dummy) as CSSMatrixTransformsKey[];

Finally, in your code, check if your key exists in the matrixKeys array:

function setStyleAttributes(attrs: { [ k in keyof ( CSSStyleDeclaration & CSSTransformsAttr )]?: any }): void {
    if (attrs !== undefined) {
            Object.keys(attrs).forEach((key: string) => {
                if(matrixKeys.includes(key)) {
                    this.htmlElement.style[key] = attrs[key];
                }

        });
    }
}

Check this out as well.

igg
  • 2,172
  • 3
  • 10
  • 33
0

With the keyof operator e.g.

var attributeStr: keyof CSSMatrixTransforms = 'scaleX' // ok

var attributeStr: keyof CSSMatrixTransforms = 'scaleX1' // wrong
Murat Karagöz
  • 35,401
  • 16
  • 78
  • 107
  • this is what i tried: ```var randomStr = randomString(); var attributeStr: keyof CSSMatrixTransforms = randomStr ``` i got failed compile: `Type 'string' is not assignable to type '"scaleX" | "scaleY" | "skewX" | "skewY" | "translateX" | "translateY"'.` – Bao Phuc Tran Jan 13 '20 at 10:31
  • You have to define the return type of `randomString` with the same type as `attributeStr` – Murat Karagöz Jan 13 '20 at 10:35
0

I came up with the code shown below, I had to add the as to cast them but at least I no longer use the any Type anywhere now.

// we need to exclude readonly & function properties (since they're not writable props)
export type CSSStyleDeclarationReadonly = 'length' | 'parentRule' | 'getPropertyPriority' | 'getPropertyValue' | 'item' | 'removeProperty' | 'setProperty';
export type CSSStyleDeclarationWritable = keyof Omit<CSSStyleDeclaration, CSSStyleDeclarationReadonly>;

Object.keys(this.stylingCss as CSSStyleDeclaration).forEach(cssStyleKey => {
  this.elem!.style[cssStyleKey as CSSStyleDeclarationWritable] = this.stylingCss[cssStyleKey as CSSStyleDeclarationWritable];
});

There you go, no more errors and now it does Type checking correctly

ghiscoding
  • 12,308
  • 6
  • 69
  • 112