Question
Fixing Type 'string' Is Not Assignable to Type in TypeScript Union Types
Question
In TypeScript, I defined a custom type in fruit.ts like this:
export type Fruit = "Orange" | "Apple" | "Banana";
In another file, I import that type and write code like this:
let myString: string = "Banana";
let myFruit: Fruit = myString;
When I assign myString to myFruit, TypeScript shows this error:
Type 'string' is not assignable to type '"Orange" | "Apple" | "Banana"'
How can I safely assign a string to a variable of the custom type Fruit?
Short Answer
By the end of this page, you will understand why a general string cannot be assigned to a narrower union type like Fruit, how TypeScript treats string literal types, and the safe ways to convert or validate a string before using it as a custom union type.
Concept
TypeScript distinguishes between broad types and narrow types.
stringmeans any possible string value"Banana" | "Apple" | "Orange"means only one of those exact strings
Your Fruit type is a string literal union type:
type Fruit = "Orange" | "Apple" | "Banana";
This is narrower than string.
So even if a variable currently contains "Banana", if its type is declared as string, TypeScript must assume it could later contain:
"Grape"
"Car"
"Hello"
Because of that, this is not safe:
let myString: string = "Banana";
let myFruit: Fruit = myString;
Mental Model
Think of string as a huge box of all labels and Fruit as a small basket that only accepts three labels:
stringbox: any label at allFruitbasket: onlyOrange,Apple, orBanana
If someone hands you a value from the huge string box, you cannot just drop it into the Fruit basket unless you check that it is one of the allowed labels.
Another way to think about it:
string= "I know this is text"Fruit= "I know this is one of these exact texts"
TypeScript needs that extra certainty before allowing the assignment.
Syntax and Examples
The main ways to work with this are:
1. Declare the value as Fruit from the start
If the value is known to be valid, give it the narrower type immediately.
type Fruit = "Orange" | "Apple" | "Banana";
let myFruit: Fruit = "Banana";
This works because "Banana" is one of the allowed values.
2. Let TypeScript infer the literal type
If you use const, TypeScript usually keeps the exact literal value.
type Fruit = "Orange" | "Apple" | "Banana";
const myString = "Banana";
const myFruit: Fruit = myString;
Here, myString is inferred as "Banana", not string, so the assignment is valid.
3. Validate the string before assigning
Step by Step Execution
Consider this example:
type Fruit = "Orange" | "Apple" | "Banana";
function isFruit(value: string): value is Fruit {
return value === "Orange" || value === "Apple" || value === "Banana";
}
const input: string = "Banana";
let selectedFruit: Fruit | undefined;
if (isFruit(input)) {
selectedFruit = input;
}
Step by step:
Fruitis defined as a union of three exact strings.isFruittakes anystring.- Inside
isFruit, the function checks whether the value matches one of the allowed fruit names. inputis typed asstring, so TypeScript does not assume it is a valid .
Real World Use Cases
String literal union types are common in real applications.
API response statuses
type Status = "idle" | "loading" | "success" | "error";
This prevents invalid states like "done" when your app only supports four known statuses.
User roles
type Role = "admin" | "editor" | "viewer";
You can validate input from a database or request before assigning it to Role.
Configuration values
type Theme = "light" | "dark";
This ensures only supported theme names are used.
Form options
type PaymentMethod = "card" | "cash" | "bank-transfer";
Real Codebase Usage
In real codebases, developers usually avoid assigning plain string values directly to narrow union types unless the value is already trusted.
Common patterns
Guard clauses for validation
function parseFruit(value: string): Fruit | undefined {
if (value !== "Orange" && value !== "Apple" && value !== "Banana") {
return undefined;
}
return value;
}
This pattern is common when processing request data, query parameters, or form input.
Early return for invalid input
function handleFruit(value: string) {
if (value !== "Orange" && value !== "Apple" && value !== "Banana") {
throw new Error("Invalid fruit");
}
const fruit: Fruit = value;
console.(fruit);
}
Common Mistakes
Mistake 1: Declaring a literal as string
let myString: string = "Banana";
let myFruit: Fruit = myString; // Error
Why it happens:
- You told TypeScript this variable can hold any string.
How to avoid it:
- Use
constwhen the value does not change. - Use
Fruitdirectly if it should always be a valid fruit.
const myString = "Banana";
const myFruit: Fruit = myString;
Mistake 2: Using as Fruit without validation
const input: string = "Grape";
const fruit = input as Fruit;
Why it is dangerous:
Comparisons
| Concept | Meaning | Example | Safe for dynamic input? |
|---|---|---|---|
string | Any text value | let x: string = "Banana" | No, too broad |
| String literal | One exact text value | const x = "Banana" | Only if the value is known |
| Union of literals | One of a fixed set | `type Fruit = "Apple" | "Banana"` |
| Type assertion | Force TypeScript to trust you | x as Fruit | Risky |
| Type guard | Check value before narrowing | isFruit(x) |
Cheat Sheet
type Fruit = "Orange" | "Apple" | "Banana";
Rules
stringis broader thanFruit- You cannot assign
stringtoFruitwithout narrowing const x = "Banana"is often inferred as the literal"Banana"let x = "Banana"is usually inferred more broadlyas Fruitbypasses checking and should be used carefully
Safe patterns
Known valid value
const fruit: Fruit = "Banana";
Narrow via inference
const value = "Banana";
const fruit: Fruit = value;
Narrow via validation
FAQ
Why does TypeScript reject a string that contains "Banana"?
Because the variable is typed as string, which means it could contain any string, not just allowed fruit values.
Can I force the assignment with as Fruit?
Yes, but it only silences TypeScript. It does not validate the actual value at runtime.
What is the safest way to convert a string to Fruit?
Use a type guard or validation function that checks the string before assigning it.
Why does const myString = "Banana" work better than let myString: string = "Banana"?
const usually preserves the exact literal type, while string is a broader type.
When should I use a union type like Fruit?
Use it when only a fixed set of string values is valid, such as statuses, roles, modes, or options.
Does TypeScript validate union types at runtime?
No. TypeScript only checks types during development and compilation. Runtime validation must be written explicitly.
Mini Project
Description
Build a small TypeScript utility that accepts a string from an external source and safely turns it into a valid Fruit value. This demonstrates how union types and type guards work together in real code where input cannot be trusted automatically.
Goal
Create a function that validates a string and returns a safe Fruit result or an error message.
Requirements
- Define a
Fruitunion type with"Orange","Apple", and"Banana". - Create a type guard function to check whether a string is a valid fruit.
- Write a parser function that accepts a
stringand returns either aFruitorundefined. - Test the parser with both valid and invalid input values.
- Print clear output showing which values were accepted and which were rejected.
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.
Angular Dependency Injection: Fix "Can't Resolve All Parameters for Component" Errors
Learn why Angular shows "Can't resolve all parameters for component" and how to fix service injection issues in components.