Type 'string | number | boolean' is not assignable to type 'undefined'. Type 'string' is not assignable to type 'undefined'.ts(2322) - javascript

I'm trying to create a partial object that only has certain fields of the full object that meet a criteria. However, I get the subject typescript error message when I try to assign the property. I created a test module to illustrate the concept/problem. Note that this is only to illustrate the problem. It is not the actual code.
type FullObject = {
id: number
name: string
active: boolean
}
type PartialObject = Partial<FullObject>
const myFullObj: FullObject = {
id: 1,
name: 'First Object',
active: true,
}
const myPartialObj: PartialObject = {}
let k: keyof PartialObject
for (k in myFullObj) {
if (myFullObj[k] !== undefined) myPartialObj[k] = myFullObj[k] // Error here
if (k === 'name') myPartialObj[k] = myFullObj[k] // No error here
}
Note that it is only the first "if" statement that has the error. After some research and trying various things, I worked around the problem by initializing the partial object to the full object and then deleting properties that did not meet a criteria. Since this is a backwards way of solving the problem, I would prefer to create the partial object with properties that meet criteria.

I came up with the following solution. It clearly illustrates what I am trying to do: if a criteria is met with a source object property, then copy that property into the partial destination object. In this example I'm using "not undefined" as the criteria. In the real code the criteria is more complex.
type FullObject = {
id: number
name: string
active: boolean
}
type PartialObject = Partial<FullObject>
const myFullObj: FullObject = {
id: 1,
name: 'First Object',
active: true,
}
let myPartialObj: PartialObject = {}
let k: keyof PartialObject
for (k in myFullObj) {
if (myFullObj[k] !== undefined) myPartialObj = { ...myPartialObj, ...Object.fromEntries([[k, myFullObj[k]]]) }
}
console.log(JSON.stringify(myPartialObj, null, ' '))
It seems that there must be a better way to accomplish this. However, the example illustrates what is intended.

Related

Why missing properties are not checked while using type assertion?

TS playground
Why when I use as with type then required object keys are not checked anymore? For example I have Person type where I need to have name and age.
type Person = {
name: string,
age: number,
}
const demo: Person = { // OK - missing 'age' property error
name: 'test',
}
const demo2 = { // no missing 'age' property error?
name: 'test',
} as Person
const demo3 = { // no missing 'age' property error?
people: [{
name: 'test',
}] as Person[]
}
With type assertion you are actually "forcing" the data to be considered as some type so, as long as the two types overlap in some way TypeScript allows you to do this.
Sometimes TypeScript also reminds you (when the data and the type does not overlap at all) to declare that "you are sure of what you are doing" declaring the data as unknown and then as the wanted type, like in this case:
// No overlap at all
const demo3 = {
people: [{
foo: 'test',
}] as Person[]
}
// telling TypeScript "yes, I'm sure of what I am doing"
const demo4 = {
people: [{
foo: 'test',
}] as unknown as Person[] // also "as any as Person[]" will work
}

Typescript - typing array methods

I am having trouble typing array methods
const person = students.findIndex((student) => student.id === 23)
The first error I get is on student element in the brackets
const person = students.findIndex((student) => student.id === 23)
TS7006: Parameter 'student' implicitly has an 'any' type.
This can be fixed by
const person = students.findIndex((student:any) => student.id === 23)
Which is not great so I try
const person = students.findIndex((student:Object) => student.id === 23)
but I get
TS2339: Property 'id' does not exist on type 'Object'
I assume because Object is a generic Type.
What is best practice here? I do hundreds of these with filter, map, reduce do I have to define the element being processed by the method in typesscript?
Create Student interface and provide the same in place of Object and any other places for type check. I recommend to mention type explicitly(good coding practice) even though TS can do implicit type check.
interface Student {
id: number;
name: string;
// other properties...
}
const students: Student[] = [{
id: 123,
name: "test123"
},
{
id: 456,
name: "test456"
}]; // Suppose this is the data example
const person = students.findIndex((student: Student) => student.id === 23)
It's better to use TypeScript's implicit typing.
Create a type
type Student = {
name: string;
};
const students: Student[] = [
{ name: 'ABC' },
{ name: 'DEF' }
];
const person = students.findIndex( student => student.name === 'ABC' ); // 0
When you declare your students variable as an array of Student type, your array prototype will extend itself to include Student types. So your array prototype methods will consider Student as their type as long as you call those methods on the students array.

Array assigned to variable is 'undefined'

I got multiple checkboxes to select variants.
And I am getting checked data from like this:
get violCategoriesFormArraySelectedIds(): string[] {
return this.violCategories
.filter((cat, catIdx) => this.violCategoriesFormArr.controls.some((control, controlIdx) => catIdx === controlIdx && control.value))
.map(cat => cat.value);
}
then console.log output is:
console.log('arrViol: '+this.violCategoriesFormArraySelectedIds);
arrViol: Unauthorized access to information, Getting confidential
information by supposedly trustworthy person (phishing)
then I am trying to assign it to my varaible like:
this.violation = this.violCategoriesFormArraySelectedIds;
but got error
Type 'string[]' is not assignable to type 'string'
this is my variable. I tried also violation: string;
violation = '';
I tried also
this.violation = this.violCategoriesFormArraySelectedIds.toString();
and it "works" console.log is good but now..
I want assign this.violation to my interface (violation?: any; (I just tried any but nothing))
answers: Answers = {
id: undefined,
name: '',
date: '',
time: '',
violation: this.violation,
description: '',
numofpeople: 0,
};
but this.violation despite the correct output in console.log
is
undefined
when I am trying to do console.log(this.answers.violation)
I do not need put into my MySql an array output. I want just string separated by commas.
You are looking for this:
this.violation = this.violCategoriesFormArraySelectedIds.join(', ')
This will convert an array of strings into a string with the elements of the array separated by commas.
Then you need to assign it to this.answers:
this.answers = {
id: undefined,
name: '',
date: '',
time: '',
violation: this.violation,
description: '',
numofpeople: 0,
};
console.log(this.answers)

How to check the type of these three values with TypeScript interfaces?

The following code often breaks because label, description, details have the wrong type.
So I want to make sure they are being fed the right arguments:
label should be string.
description should be string or array of strings.
details should be string.
This is my first time using TypeScript, so I checked the official interface docs. And included an interface:
const quickPick = vscode.window.createQuickPick()
const response = await fetch(`https://api.datamuse.com/words?ml=${text.replace(" ", "+")}`)
const data = await response.json() // [{"word": "close", "score": 123, "tags": ["n", "adj"]}
interface Item {
label: string
description: string[] || string
detail: string
}
quickPick.items = data.map((item: Item) => {
return {
label: item.word,
description: item.tags ? item.tags.join(', ') : 'no tags',
detail: `Relevance: ${item.score.toString()}`
}
})
However, Visual Studio Code is showing me a bunch of errors. So I'm assuming my code isn't even proper TypeScript code.
What's the correct way of doing this?
Update: I tried the suggestion below, but now I have this error:
As #cartant mentioned in the comments, you union type declaration is wrong, it should be separated by | instead of ||
interface Item {
label: string;
description: string[] | string;
detail: string;
}
It also looks that the map's lambda definition is incorrect. You defined it as (item: Item) => { ... } which means that input parameter type is Item. But, I guess that you intended to return that type, so definition should look like: (item): Item => { ... }
quickPick.items = data.map((item): Item => ({
label: item.word,
description: item.tags ? item.tags.join(', ') : 'no tags',
detail: `Relevance: ${item.score.toString()}`
}))
I've also changed () => { return { ... }; } to () => ({ ... }), it's just a more convenient way to write such expression.

Flow: Inference error in for-of loop

I have a code this is trying to validate object with attributes id and _name for specified type. Attribute id should be a number and name should be a string as declared in FooT type.
function number(value: mixed): number {
if (typeof value === "number") return value
throw new TypeError("number required")
}
function string(value: mixed): string {
if (typeof value === "string") return value
throw new TypeError("string required")
}
function objectOf(attrs, value) {
const obj = {}
for (const key of Object.keys(attrs)) {
const typeFn = attrs[key]
obj[key] = typeFn(value[key])
}
return obj
}
type FooT = {
id: number,
name: string
}
const fooT: FooT = objectOf(
{
id: number,
name: string
},
{
id: 1,
name: "Foo"
}
)
Running flow shows this error. For some reason inferred return type of typeFn is not correctly determined in for-of loop when accessing object attribute values dynamically.
Cannot assign objectOf(...) to fooT because:
• string [1] is incompatible with number [2] in property id.
• number [3] is incompatible with string [4] in property name.
[3] 3│ function number(value: mixed): number {
:
[1] 8│ function string(value: mixed): string {
:
[2] 22│ id: number,
[4] 23│ name: string
24│ }
25│
26│ const fooT: FooT = objectOf(
27│ {
28│ id: number,
29│ name: string
30│ },
31│ {
32│ id: 1,
33│ name: "Foo"
34│ }
35│ )
36│
Is this an issue with flow or am I missing something?
It looks like you are running into issue #935 Type of object value not inferred correctly in for-in loop. You should be able to use the suppress_comment config and just put $FlowFixMe in the code to tell Flow to ignore that.

Categories

Resources