1

I have functions to validate forms, I want the function to be in a separate file, but I have a type problem for validationRequirements[fieldName] i getting error Type 'keyof T1' cannot be used to index type '{ name: (value: string) => boolean; }'.

Here is my validations requirements:

const validationRequirements = {
    name: (value: string) => false, //mock
}

Interface for createValidationObj:

interface CreateValidationObjI<T1> {
    validation: T1
    fieldName: keyof T1
    value: string
}

Here I call a function to update the validation object, I pass validationI as typescript argument then i use it as generic:

const onChangeValidation = ({ value, fieldName }: { value: string; fieldName: keyof typeof validation }) => {
    const validationObj = createValidationObj<validationI>({ validation, fieldName, value })
}

Here is the validation function, I want that function in separate:

const createValidationObj = <T1 extends {}>({ validation, fieldName, value }: CreateValidationObjI<T1>) => ({
    ...validation,
    [fieldName]: {
        ...validation[fieldName],
        isValid: validationRequirements[fieldName](value), <-- Type 'keyof T1' cannot be used to index type '{ name: (value: string) => boolean; }'.
        isTouched: true,
    },
})

Link to example: Example

2
  • 1
    Is it intentional that T1 extends {} rather than the referenced validationRequirements? Commented Mar 4, 2021 at 9:01
  • Even if T1 extends the interface validationI, the code will not work because that implies T1 may have some extra keys different from validationI. Either T1 must have same or lesser(subset) keys than validationI or assigning might have reverse (but it is not the requirement of the code) Commented Mar 4, 2021 at 9:07

1 Answer 1

1

Problem

Consider this code. Any field from object of type Parent is assignable to object of type Child (as soon as their type matches). This is because Parent has keys "a" | "b". So, definitely, parentObject has keys that childObject has but vice-versa is not true as parentObject is missing key c from the childObject.

So, in short, the keys of the object whose fields we are assigning must be subset of keys the object to which we are assigning.

The keys of type {} is an empty set, therefore, the error in function f3.

interface Parent {
    a: string
    b: string
}

interface Child {
    a: string
    b: string
    c: string
}

const parentObject: Parent = {
    a: "abc",
    b: "string"
}

const childObject: Child = {
    a: "abc",
    b: "string",
    c: "string"
}

type KeysOfParent = keyof Parent // "a" | "b"
type KeysOfChild = keyof Child // "a" | "b" | "c"

function f(field: keyof Parent) {
    childObject[field] = "abc" // OK
}

function f2(field: keyof Child) {
    parentObject[field] = "abc" // ERROR
}

function f3<T extends {}>(field: keyof T) {
    parentObject[field] = "abc" // ERROR
}

Playground

So, in the question, in function createValidationObj, fieldName has keys - keyof {} which is definitely not asubset of keys of validationRequirements ("name"). So the error.

Solution

If it does not violate your requirements, remove T1 at all.

interface validationI {
    name: {
        isValid: boolean
        isTouched: boolean
        errorMsg: string
    },
    field2: {
        isValid: boolean
        isTouched: boolean
        errorMsg: string
    },
}

const validation: validationI = {
    name: {
        isValid: false,
        isTouched: false,
        errorMsg: "Min 3 chars",
    },
    field2: {
        isValid: false,
        isTouched: false,
        errorMsg: "Min 3 chars",
    },
}
type X = keyof typeof validation

const validationRequirements = {
    name: (value: string) => false, //mock
    field2: (value: string) => false, //mock
}

interface CreateValidationObjI<T1 extends validationI> {
    validation: T1
    fieldName: keyof T1
    value: string
}

const createValidationObj = ({validation, fieldName, value}: CreateValidationObjI<validationI>) => ({
    ...validation,
    [fieldName]: {
        ...validation[fieldName],
        isValid: validationRequirements[fieldName](value),
        isTouched: true,
    }
})

const onChangeValidation = ({ value, fieldName }: { value: string; fieldName: keyof typeof validation }) => {
    const validationObj = createValidationObj({ validation, fieldName, value })
}

Playground

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.