This code:
interface Dog {
name?: string;
size?: number;
}
const entity: Dog = {
name: 'max',
}
const name: string = entity.name;
Causes this error:
Type 'string | undefined' is not assignable to type 'string'.
I can avoid the error by removing the entity
type:
interface Dog {
name?: string;
size?: number;
}
const entity = {
name: 'max',
}
const name: string = entity.name;
But then I lose the auto-complete feature.
Is there a way to win both? To have autocomplete, and let the code know which keys are there based on the initializer?
E.g. using Required<Dog>
isn't a good solution because I don't want to initialize size
. The real use-case I have actually has a much bigger interface.
CodePudding user response:
You can infer it with help of extra function:
interface Dog {
name?: string;
size?: number;
}
type IsValidDog<Animal> = Required<Dog> extends Animal ? Animal : never;
const builder = <Animal extends Dog>(animal: IsValidDog<Animal>) => animal
/**
* Ok
*/
const result = builder({
name: 'max',
})
result.name // ok
result.size // expected error
const result2 = builder({
name: 'max',
size: 42
})
result2.name // ok
result2.size // ok
/**
* Error
*/
const result3 = builder({ name: 'Sharky', unknown: 2 }) // expected error
const result4 = builder({ nAme: 'Sharky', size: 2 }) // expected error
const result5 = builder({ name: 'Sharky', size: 2, surname: 'Doe' }) // expected error
builder
function expects/allows exact a Dog
object. It allows use less properties, because all of them are optional but it disaalow any extra properties to bu used, see result5
.
IsValidDog
- checks whether Dog
with all required props extends passed object interface. If you pass an object with some extra properties, this check will fail.
You can find more validation techniques in my blog
CodePudding user response:
What I do is to define a generic identity check function that can be used for any interfaces:
function identityCheck<T = never>() {
return <I>(input: I & T) => input as I;
}
then create a concrete check-function for the Dog
interface:
const dogIdentity = identityCheck<Dog>();
finally use it to create the constant:
const myDog = dogIdentity({
name: 'bello',
})
// name is of type string
myDog.name.slice();
CodePudding user response:
Since the entity is Dog, name of it should be string | undefined. You should:
- tell the compiler that it's never undefined
- validate name as usual
interface Dog {
name?: string;
size?: number;
}
const entity: Dog = {
name: 'max',
}
// tell compiler it's never undefined
const name1: string = entity.name!;
if(entity.name) {
const name = entity.name;
...your next codes...
}
// tricky tips
const name3: string = entity.name || "";
const name4: string = entity.name || "NEVER_HAPPEND";