Skip to content

Commit

Permalink
Improve TypeScript types of partial character details
Browse files Browse the repository at this point in the history
Make the types more accurately represent the actual situation with
multiple possible response shapes for the query.
  • Loading branch information
JoosepAlviste committed Jan 17, 2023
1 parent a3b3aac commit 01c25e6
Showing 1 changed file with 48 additions and 2 deletions.
50 changes: 48 additions & 2 deletions src/components/DetailView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
{{ character.name }} (<router-link to="/">Back to list</router-link>)
</h2>
<img v-if="character.image" :src="character.image" class="image" />
<template v-if="character.episode">
<template v-if="areDetailsLoaded(character)">
<div>Gender: {{ character.gender }}</div>
<div>Status: {{ character.status }}</div>
<div>Species: {{ character.species }}</div>
Expand All @@ -27,12 +27,43 @@
import { useQuery } from '@vue/apollo-composable';
import { computed } from 'vue';
import { graphql } from '../gql';
import {
CharacterQuery,
CharacterQueryVariables,
CharactersQuery,
} from '../gql/graphql';

const props = defineProps<{
id: string;
}>();

const { result, loading } = useQuery(
/**
* Type of a single character from the list query - our "partial" data.
*/
type CharacterWithPartialData = NonNullable<
NonNullable<NonNullable<CharactersQuery['characters']>['results']>[number]
>;

/**
* Type of a character with all the requested fields set - our "full" data.
*/
type CharacterWithFullData = NonNullable<CharacterQuery['character']>;

/**
* We need to manually type the query response since GraphQL Code Generator is
* not smart enough to type such a complicated workflow for us.
*
* The response can include either the "partial" data or the "full" data.
*/
type PartialCharacterQueryResponse = {
__typename?: 'Query';
character?: CharacterWithPartialData | CharacterWithFullData | null;
};

const { result, loading } = useQuery<
PartialCharacterQueryResponse,
CharacterQueryVariables
>(
graphql(`
query character($characterId: ID!) {
character(id: $characterId) {
Expand Down Expand Up @@ -61,6 +92,21 @@ const { result, loading } = useQuery(
);

const character = computed(() => result.value?.character);

/**
* Check if the details for the character have been loaded.
*
* Make use of a custom type guard to make it easier to determine which type of
* data we're dealing with in the template.
*
* Read more about TypeScript type predicates here:
* https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates
*/
const areDetailsLoaded = (
character: NonNullable<PartialCharacterQueryResponse['character']>
): character is CharacterWithFullData => {
return Boolean('episode' in character);
};
</script>

<style scoped>
Expand Down

0 comments on commit 01c25e6

Please sign in to comment.