4

I'm trying to define custom typings for a navigation library using TypeScript, and I haven't wrapped my head around creating a navigate function that takes the Screen's name as a first argument, and the Screen's properties as a second argument while still being type safe.

I'm trying to:

  1. Safely type the screen name (keyof Stack) as a first argument
  2. Safely encapsulate my two navigation stacks (navigate<LoginStack>('HomeScreen') should never work, only the screens from LoginStack should be possible)
  3. Use the correct arguments for the corresponding screen name (keyof Stack) as the second argument

My current approach:

ScreenDefinitions.ts:

export interface LoginStackScreens {
    LoginScreen: BaseProps & { isHeadless?: boolean };
    ForgotPasswordScreen: BaseProps & { email: string };
}
export interface HomeStackScreens {
    HomeScreen: BaseProps & { isHeadless?: boolean };
    SavedScreen: BaseProps;
    ChatsListScreen: BaseProps;
    MyProfileScreen: BaseProps;
    PostDetailsScreen: BaseProps & {
        post: Post;
    };
    // ... some more
}

Navigation.ts:

type ValueOf<T> = T[keyof T];
function navigate<Stack extends HomeStackScreens | LoginStackScreens>(screenName: keyof Stack, props: ValueOf<Stack>): void {
    // Navigation logic
}

navigate<HomeStackScreens>('HomeScreen', {});

While I achieved points 1. and 2., the second parameter (arguments) aren't safely typed as they contain all values from the HomeStackScreens stack:

Screenshot of VSCode showing all types of arguments

Whereas I only want to have BaseProps & { isHeadless?: boolean } in this case (See HomeStackScreens definition)

1 Answer 1

8

Try this!

function navigate<Stack extends {}>(
  screenName: keyof Stack,
  props: Stack[typeof screenName]
) {
  // Implementation
}

Example,

type SomeStack = {
  someScreen: {
    test: string
  }
}

navigate<SomeStack>('someScreen', {
  test: 5 // Fails - should be a string!
})
Sign up to request clarification or add additional context in comments.

8 Comments

This doesn't work if I define the NavigationProp interface with it's functions, see: imgur.com/a/i9Ck3pv
It looks like TypeScript is fine with it, and ESLint is getting confused. Maybe just turn off ESLint for that line!
You're right, only ESLint's fault. But the typing for the arguments still don't work, somehow the types are resolved as every argument for every route in the Stack OR (|) unknown. See: imgur.com/a/LorEtuu
|

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.