Code Bug Fix: TypeScript typeof and class constructor problem

Original Source Link

I have a map like this:

  const map = {
    a: ClassA,
    b: ClassB,
    c: ClassC,
  }

which maps to different classes. and I have this function, which is returning instance of a class mapped by a key in map object:

const conf = {};

type VersionMap = typeof map;
type Versions = keyof VersionMap;

const getter = <T extends Versions>(ver: T): VersionMap[T] => new map[ver](conf);

But problem is that typeof is referencing to class constructor, not an instance so I got an error. How can I solve that issue without creating second type?

I think there is some small misconception here on how classes are typed.
Let’s assume, for abbreviation here, that we have two classes ClassA and ClassB.

class ClassA {
  constructor() {
    console.log("Class A");
  }
}

class ClassB {
  constructor() {
    console.log("Class B");
  }
}

Creating the map object is easy, but I think, it would have been easier to first describe an interface on what this object is supposed to hold. But as in your example, I proceed without one.

const map = {
  a: ClassA,
  b: ClassB,
};

Now comes the tricky part – Typings.
First I want to extract all keys of the map object.
Therefore I’ll create a new type called: Keys

// Type contains: "a" | "b"
type Keys = keyof typeof map;

Next I need all associated values for each key.
To achieve this, create a new type at the index of Keys.

// Type contains: typeof ClassA | typeof ClassB
type Values = typeof map[Keys];

The getter function should return an instance of a class T. But the Values type contains all classes as typeof ClassA | typeof ClassB which is equal to new() => ClassA | new() => ClassB.
Thus the type of ClassA | ClassB is not compatible.

So let’s craft a seperate type to get rid of this. The type should return the instance type of a class – just ClassA or ClassB.
Or so to say, the return type of new SomeClass() operation.

type ReturnType<T> = T extends new() => infer R ? R : never;

If you want to read up on conditional types in TypeScript, I highly recommend reading this blog post: https://mariusschulz.com/blog/conditional-types-in-typescript
Let’s add the final piece:

const conf = {};
const getter = (ver: Keys): ReturnType<Values> => new map[ver]();

FYI: I did change the naming of VersionMap to Values and Version to Keys as I think it’s easier to understand what each of those types is supposed to do. But feel free to change them to your needs.

Tagged :

Code Bug Fix: Can I insert a string into a URL only if the string is not empty?

Original Source Link

I try to get data from a database. There are optional parts that I can include to make the search more specific.

I have an object like this:

{
  title: "wonderland",
  aliases: "",
  ...
}

Now I want to create an URL for the GET-Request

getResults(obj){
    return this.http.get(`${this.url}/title=${obj.title}&aliases=${obj.aliases}`)
}

Now I want to drop the part &aliases=${obj.aliases} because, as you see above, the property aliases is in this case empty.
If the property title were empty, I would want to drop title=${obj.title}.

How would you do this?

Try this. Better than what you are trying to do

let object = {
  title: "wonderland",
  aliases: ""
}

function addOptionalParameters(urlPath) {
  let count = 0;

  for(let param in object){
    if (object[param]) {
      if (count === 0 ) {
        urlPath = `${urlPath}/${param}=${object[param]}`
      } else {
        urlPath = `${urlPath}&${param}=${object[param]}`
      }
      count ++;
    }
  }

  return urlPath;
}

function getResults(obj) {
  let urlPath = `${this.url}`
  urlPath = addOptionalParameters(urlPath)

  return this.http.get(`${urlPath}`)
}

You could do something like this:


getAliasQuery(aliases) {
    if (aliases) return '&aliases=${aliases}';
    return '';
}

getResults(obj){
    return this.http.get(`${this.url}/title=${obj.title}${getAliasQuery(obj.aliases)}`)
}

Tagged : / / / /

Code Bug Fix: Object is possibly undefined is not accepted with question mark

Original Source Link

Consider the following ocde:

app-config.json

{
  "auth": {
    "clientId": "acb610688b49",
  },
  "cache": {
    "cacheLocation": "localStorage"
  },
  "scopes": {
    "loginRequest": ["openid", "profile", "user.read"]
  },
  "resources": {
    "gatewayApi": {
      "resourceUri": "https://localhost:44351/api",
      "resourceScope": ["api://0e01a2d8/access_as_user"]
    }
  }
}

authService.js

import { isInternetExplorer } from 'src/services/utils/utilsService'
import * as Msal from 'msal'
import * as configJson from 'src/app-config.json'

type resourceType = {
  resourceUri: string
  resourceScope: string | string[]
}

type resourcesType = {
  [key: string]: resourceType
}

interface JsonConfigInterface extends Msal.Configuration {
  scopes: {
    loginRequest: string[]
  }
  resources: resourcesType
}

const config: JsonConfigInterface = configJson as JsonConfigInterface

function MSALConfigFactory(): Msal.Configuration {
  return {
    auth: {
      clientId: config.auth.clientId,
    },
    cache: {
      cacheLocation?: config.cache.cacheLocation as Msal.CacheLocation,
    },
  }
}

The reported error for cacheLocation?: config.cache.cacheLocation as Msal.CacheLocation is:

(property) cache?: CacheOptions | undefined Object is possibly
‘undefined’.ts(2532)

When looking at the msal documentation it says:

export type CacheLocation = "localStorage" | "sessionStorage";

export type CacheOptions = {
    cacheLocation?: CacheLocation;
    storeAuthStateInCookie?: boolean;
};

The question mark in cacheLocation? indicates that this parameter is optional, which is fine. It can or cannot be defined within the JSON file. So I do not understand why TypeScript complains that it can be undefined when that is an accepted value? Of course, the TS null check is in place, but should it not allow this as there is a question mark?

The current workaround for now is below, but I don’t know if that is the correct approach:

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
cacheLocation: config.cache!.cacheLocation as Msal.CacheLocation,

I’m still a beginner, so thank you for your help.

Typescript is trying to tell you that this property access might result in an error:

config.cache.cacheLocation

If either config or config.cache turn out undefined, an exception will be raised.

You can do this:

config?.cache?.cacheLocation

It seems this is the type:

export type Configuration = {
    auth?: BrowserAuthOptions,
    cache?: CacheOptions,
    system?: BrowserSystemOptions
};

Tagged : /

Code Bug Fix: How to check if one array of objects value with another array of objects value, if found then hide something?

Original Source Link

I am trying to check one array of objects value exists in another array of objects value (Means if both values matches then i am disabling the table row). So for that how do i check both array of objects values. See my below two array objects.

const array1 = [
{
id: "8a8080877134b405017134c1adb3004f",
name: "AWS API1"
},
{
id: "8a8080b6720900d301720935a7120000",
name: "AWS API3"
},
{
id: "8a80808271773317017177848a5106d1",
name: "AZURE API1"
}
];

const array2 = [
{
apiId: "8a8080877134b405017134c1adb4444f",
apiName: "AWS API2"
},
{
apiId: "ass34dgdfgfdgfdg35435ERF",
apiName: "AZURE API1"
},
{
apiId: "dfdfdaggfdgdfg4324564",
apiName: "AWS API1"
}
];

From the above 2 array of objects, i want to check if the array2.apiName matches with array1.name then i want to disable table row. So how to achieve it?

See the below code.

const r = array1.filter((elem) => !array2.find(({ apiName }) => elem.name === apiName));
console.log(r);

You could use array some method on both the arrays and introduce a new property disable into the first array. And set disable property to true if it’s name is present in second array’s apiName property.

And in the template use the disable property to disable/enable the rows.

But beware, the some method will iterate each element of the first array with all the elements of the second array. So it could be a relatively heavy process for considerably large arrays.

var array1 = [
  { id: "8a8080877134b405017134c1adb3004f", name: "AWS API1" },
  { id: "8a8080b6720900d301720935a7120000", name: "AWS API3" },
  { id: "8a80808271773317017177848a5106d1", name: "AZURE API1" }
];

var array2 = [
  { apiId: "8a8080877134b405017134c1adb4444f", apiName: "AWS API2" },
  { apiId: "ass34dgdfgfdgfdg35435ERF", apiName: "AZURE API1" },
  { apiId: "dfdfdaggfdgdfg4324564", apiName: "AWS API1" }
];

array1.some(a1 => {
  array2.some(a2 => {
    if (a1.name === a2.apiName) {
      a1.disable = true;
    } else {
      if (a1.disable !== true) {
        a1.disable = false;
      }
    }
  })
});

console.log(array1);
Tagged : / /

Code Bug Fix: Vuetify Jest Unknown custom element

Original Source Link

I am writing unit tests for my project, using Jest. Project is based on Vue, Vuetify (1.5) and TypeScript (and vue-property-decorator).

I have basic wrapper for <v-btn>. It looks like this:

<template>
  <v-btn
    :round="!square"
    v-on="$listeners"
  >
    <slot />
  </v-btn>
</template>

<script lang="ts">
import { Vue, Prop, Component } from 'vue-property-decorator';

import { VBtn } from 'vuetify/lib';

@Component({
  components: {
    VBtn,
  },
})
export default class Btn extends Vue {
  @Prop({ default: false, type: Boolean }) private square!: boolean;
}
</script>

And I wrote basic test, just to mount this component:

import { mount } from '@vue/test-utils'
import Vue from 'vue';
import Btn from './Btn.vue';

describe('Btn', () => {

  it('should render button', () => {
    const wrapper = mount(Btn, {
      propsData: {
        onClick: () => {}
      }
    });

    console.log((Vue as any).options.components.VBtn)

    console.log('=======================');
    console.log(wrapper.html());
    console.log('=======================');
  });
});

When running test I’m getting this error:

  console.error node_modules/vue/dist/vue.runtime.common.dev.js:621
    [Vue warn]: Unknown custom element: <v-btn> - did you register the component correctly? For recursive components, make sure to provide the "name" option.

    found in

    ---> <Btn>

But I know that component is registered – it works when running the project (not test). Also, I have console log in test that checks if it is registered (console.log((Vue as any).options.components.VBtn)) and it shows that component is registered globally.

Here is my jest.config.js:

module.exports = {
  moduleFileExtensions: [
    "js",
    "ts",
    "json",
    "vue"
  ],
  moduleNameMapper: {
    "^@/(.*)$": "<rootDir>/src/$1",
    "^vuetify/lib$": "vuetify",
  },
  modulePaths: [
    "<rootDir>/src",
    "<rootDir>/node_modules"
  ],
  transform: {
    ".+\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$": "jest-transform-stub",
    "^.+\.ts?$": "ts-jest",
    ".*\.(vue)$": "vue-jest",
  },
  transformIgnorePatterns: [
    "<rootDir>/node_modules/(?!(vuetify)/)",
  ],
  testRegex: "(/__tests__/.*|(\.|/)(test|spec))\.(js?|ts?)$",
  setupFilesAfterEnv: ['<rootDir>/tests/unit/setup.ts'],
}

And setup.ts file:

import Vue from 'vue';
import Vuetify from 'vuetify';

Vue.config.productionTip = false;
Vue.use(Vuetify);

I was trying to move code from setup.ts to the test but it doesn’t changed anything. I think there may be problem with 'vue-property-decorator' but my whole project is based on it so I don’t want to change it.
Can you help me? Maybe something is wrong in my Jest transform configuration?

I managed to solve this problem. Had to change two lines of code:

In my Btn component, changed VBtn import to:
import { VBtn } from 'vuetify/lib/components/VBtn'; (imported from vuetify/lib/components/VBtn, not from vuetify/lib).

In jest.config.js update in moduleNameMapper to 'vuetify/lib(.*)': '<rootDir>/node_modules/vuetify/es5$1'.

And that is all, now it works 🙂

My whole jest.config.js:

module.exports = {
  moduleFileExtensions: [
    "js",
    "ts",
    "json",
    "vue"
  ],
  moduleNameMapper: {
    "^@/(.*)$": "<rootDir>/src/$1",
    'vuetify/lib(.*)': '<rootDir>/node_modules/vuetify/es5$1',
  },
  modulePaths: [
    "<rootDir>/src",
    "<rootDir>/node_modules"
  ],
  transform: {
    ".+\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$": "jest-transform-stub",
    "^.+\.ts?$": "ts-jest",
    ".*\.(vue)$": "vue-jest",
  },
  transformIgnorePatterns: [
    "<rootDir>/node_modules/(?!(vuetify)/)",
  ],
  testRegex: "(/__tests__/.*|(\.|/)(test|spec))\.(js?|ts?)$",
  setupFilesAfterEnv: ['<rootDir>/tests/unit/setup.ts'],
}

Tagged : / / / /

Code Bug Fix: Typescript static function to create a new class by add new properties?

Original Source Link

Here is a question about mixin a base class How to create function that returns mixin function in TypeScript?

but my purpose is add new properties to current calling class.

class A {
  static extend<P extends {}, T extends A>(
    this: T,
    properties: P & ThisType<T & P>
  ): T & P {
    // impl is ignored.
    return {
      ...this,
      ...properties,
    };
  }

  AA(): string {
    return "aa in A";
  }
}

class B extends A {
  bb: string = "bb in B";

  BB(): string {
    return "BB in B";
  }

  CC(): string {
    return "CC in B";
  }
}

// The 'this' context of type 'typeof B' is not assignable to method's 'this' of type 'A'. Property 'AA' is missing in type 'typeof B' but required in type 'A'.
const C = B.extend({
  cc: "cc in C",
  dd: function () {
    // call functions or access properties is allowed.
    console.log(this.bb);
    console.log(this.BB());

    console.log(this.cc);
    console.log(this.CC());
  },

  // abstract overwrite is required
  BB: function () {
    return "BB in C";
  },

  // overwrite parent function
  CC: function () {
    return "CC in C";
  },
});

const d = new C();
console.log(d.aa()); // aa in A
console.log(d.bb); // bb in B

The first error is

The 'this' context of type 'typeof B' is not assignable to method's 'this' of type 'A'. Property 'AA' is missing in type 'typeof B' but required in type 'A'.

If I change T extends A to T extends typeof A, the error becomes:

Property 'bb' does not exist on type 'typeof B & { cc: string; dd: () => void; BB: () => "BB in C"; CC: () => "CC in C"; }'. Did you mean 'BB'?

Tagged : /

Code Bug Fix: Try to do a unit test in TypeScript

Original Source Link

I’m trying to understand how unit tests work

I’ve got this method:

public async parseTest(diffComponents: any[]): Promise<Map<string, string[]>> {
    const result: Map<string, string[]> = new Map();
    for (let i = 0; i < diff.length; i++) {
        const element = diff[i];
        try {
            const fileContent = fs.readFileSync("changelog.txt","UTF-8");
            const lines = fileContent.split("n");
            const list = this.retrieve(lines, element);
            result.set(element.components, list);
        } catch (err) {
            return Promise.reject("ERROR: Cannot find file from components");
        }
    }
    return result;
}

The ouput of result is for example:

Map { 'toto' => [ 'toto-11' ] }

And here this is my test but it doesn’t work:

describe("parse", () => {
    test("check map result", () => {
        // Given
        const array = [{ components: "toto", newVersion: "2.2", oldVersion: "2.1" }];

        // When
        const result = new ChangelogService().parseTest(array);
        const expected = new Map();
        expected.set("toto", "toto-11");
        // Then
        expect(result).toBe(expected);   // problem in this line 
    });
});

the error of the line is :

Error: expect(received).toBe(expected) // Object.is equality
Expected: Map {"toto" => "toto-11"}
Received: {}Jest

Can you explain the problem or give a better test method?

.toBe does exact match, it means it has to be the same object.

if you want compare that objects contain the same key and values you need to use .toEqual.

Also you Map is an array, you should do the same in the expected.

And the method returns promise you need to solve.

describe("parse", () => {
    test("check map result", async () => { // <- add async
        // Given
        const array = [{ components: "toto", newVersion: "2.2", oldVersion: "2.1" }];

        // When
        const result = await (new ChangelogService().parseTest(array)); // <- add await
        const expected = new Map();
        expected.set("toto", ["toto-11"]); // <- wrap the value
        // Then
        expect(result).toEqual(expected); // <- change to toEqual
    });
});

Then jasmine will go through the result and check it key by key with expected.

Tagged : / / /

Code Bug Fix: Typescript type for querySelectorAll

Original Source Link

Due to programming and magic my editor (vscode) knows which type querySelectorAll returns if I let Typescript do the typing for me.

But describing below variable compiled‘s type manually, is too complex for me to comprehend.
How can I explicitly describe its type?


In jasmine/angular I can (abbreviated):

const fixture = TestBed.createComponent(Proxy);
const component = fixture.componentInstance;
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement; // Magic does its work and type is known.

const titleElement = compiled.querySelectorAll('h3'); // Hovering pointer over method returns the type.
expect(titleElement.innerText).toBe('a title');

But due to reasons (how jasmine/angular sets up tests with forEach) I must declare compiled beforehand. Presently I declare it let compiled: any; which makes Typescript lose all type knowledge.

let compiled: any; // <- this is the variable I want to type.

const fixture = TestBed.createComponent(Proxy);
const component = fixture.componentInstance;
fixture.detectChanges();
compiled = fixture.debugElement.nativeElement;

const titleElement = compiled.querySelectorAll('h3')[0]; // Hovering does not yield type.
expect(titleElement.innerText).toBe('a title');

Tagged : / /

Code Bug Fix: Combined Different Array with Fixed Length

Original Source Link

If my Array1 is

Array1 = [1,2,3,4,5,6,7,8,9,10] 

the Result should be the same as Combined_Array= [1,2,3,4,5,6,7,8,9,10]

if i got

Array2=[11,12,13,14,15,16,17,18,19,20]

the Resut should be Combined_Array =[1,2,3,4,5,11,12,13,14,15]

and if again i got

Array3=[21,22,23,24,25,26,27,28,19,30]

The Combined_array = [1,2,3,11,12,13,21,22,23,24]

and so on , Doesnt matter how much Array’s i want that it should give me a Combined_Array from all the different Array with Fixed Length

Need a Function to make this work .

You could take a closure over the collected arrays and retuen an array of the parts which are difined by the count of arrays.

const
    getCombined = (a) => {
        const allArrays = [];

        return (b => {
            allArrays.push(b);
            let i = 0,
                p = Math.floor(10 / allArrays.length),
                result = [];

            while (i < allArrays.length) result.push(...allArrays[i++].slice(0, p));
            while (result.length < 10) result.push(allArrays[allArrays.length - 1][p++]);
            return result;
        });
    };

var c = [],
    add = getCombined(c);

c = add([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
console.log(...c); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

c = add([11, 12, 13, 14, 15, 16, 17, 18, 19, 20]);
console.log(...c); // [1, 2, 3, 4, 5, 11, 12, 13, 14, 15]

c = add([21, 22, 23, 24, 25, 26, 27, 28, 29, 30]);
console.log(...c); // [1, 2, 3, 11, 12, 13, 21, 22, 23, 24]

You need to consider many corner cases (if result array length exceeds given arrays count, if given arrays length differs and so on).
This will work for the simple scenario:

const arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const arr2 = [11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
const arr3 = [21, 22, 23, 24, 25, 26, 27, 28, 19, 30];

const combineArrays = (arr, length) => {
  let elementsCount = Math.floor(length / arr.length);

  const result = arr.reduce((acc, el) =>
    acc.concat(el.slice(0, elementsCount)), []);

  while (result.length < length)
    result.push(...arr.pop().slice(elementsCount, ++elementsCount));

  return result;
};

const result1 = combineArrays([arr1], 10); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
const result2 = combineArrays([arr1, arr2], 10); // [1, 2, 3, 4, 5, 11, 12, 13, 14, 15]
const result3 = combineArrays([arr1, arr2, arr3], 10); // [1, 2, 3, 11, 12, 13, 21, 22, 23, 24]

Tagged : / / /

Code Bug Fix: Fetching data in one field with respect to another field

Original Source Link

I have a project where on front end i want to implement dependable fields for example if i enter or select particular employee ID in one field the other field should automatically fetch employee name with respect that particular selected employee ID . Please Help

Tagged : / / / /