Flow: Inference error in for-of loop - javascript

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.

Related

How can you declare a dictionary with keys of any type?

I have a dictionary where keys can be any possible value (including dates, objects, etc). I tried declaring it with { [x: any ]: number }, but I get the error "An index signature parameter type must be 'string', 'number', 'symbol', or a template literal type." What would be the best way to approach this in Typescript?
// Aggregates the counts for various values of a particular key. Read-only. Does not mutate the dataset.
count = (field: keyof T) => {
var counter: { [x: any]: number } = {}
this.data.forEach( x => {
let value = x[field]
if( counter[value] ){
counter[value]++
}else{
counter[value] = 1
}
})
return counter
}

yup.js object validation, allow any key but values must be string or string array

I am using https://github.com/jquense/yup#yup
I want to have an object validation schema for:
subObjectField: {
[thisKeyCanBeAnyString]: string | string[] // allow string or array of strings
}
I cannot find an example or a starting point to achieve this, any ideas?
I've put together a function which makes this easy:
export const objectOf = (schema) => ({
name: 'objectOf',
exclusive: false,
message: "Object values don't match the given schema",
test: value => {
return value === null || Object.values(value).every(schema.isValidSync(value));
}
});
example:
yup.object().test(objectOf(yup.number())).nullable()
this successfully passes for null and for objects of numbers like { foo: 52, bar: -12 }

Mongoose automatically change the type of the value

In mongoose.model, I have chosen the type of name to be a string and the type of age to be a number, but when I enter a number as the value of name, I don't get an error and the same thing happens when I use something like '18' as the value of age.
Here is the code:
const User = mongoose.model('User', {
name: { type: String },
age: { type: Number }
});
const me = new User({
name: 12,
age: '18'
});
me.save().then(() => console.log(me)).catch(error => console.log(error));
Mongoose casts the values to the corresponding type, if it fails a CastError is thrown, from the doc:
Before running validators, Mongoose attempts to coerce values to the
correct type. This process is called casting the document. If casting
fails for a given path, the error.errors object will contain a CastError object.
You can try this by given age the value 'aa' for example.
If you want to override this behavior you can use one of the following options:
Disable casting globally: mongoose.Number.cast(false)
Disable casting just for a given path:
age: {
type: Number,
cast: false // Disable casting just for this path
},
Use a custom function:
age: {
type: Number,
cast: v => { return typeof v === 'number' && !isNaN(v) ? Number(v) : v; } // Override casting just for this path
}

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.

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

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.

Categories

Resources