0

as the title says, I have a comprehension problem with the use of Typescript, laravel and vue. Following case: I have a parent and a child component. With the parent component I make a get request to my controller which requests weather data from an external api. When they are received I validate them in the controller for the correct type safety and then give them as props to my parent component. In the parent component I also want to check these props for type safety and have given Data the type of my interface. If I now create a dummy array in the controller and change e.g. the name of the city into a number and send this dummy array to my parent component, it is not recognized as a number. Am I doing something wrong or have I configured something wrong?

Parent component:

<script lang="ts" setup>
import { defineProps, reactive, ref, onMounted } from 'vue';
import WeatherDisplay  from '@/Pages/WeatherDisplay.vue';
import type { Refs,WeatherData } from "@/interfaces/interfaces";
import { useDark, useToggle } from '@vueuse/core';
import { router } from '@inertiajs/vue3';


const isDark = useDark();
const toggleDark = useToggle(isDark);


// Define the props for the component as a type-based declaration
const props = defineProps<{
    data: WeatherData
}>
();

// This object contains all required refs
const allRefs = ref<Refs>({
        searchWeather: '',
        errorMessage: '',
}
);

// Creates a reactive array that is updated with weatherData
const weatherData = reactive<WeatherData[]>([
]);

// Function to load data from local storage
function loadDataFromLocalStorage(): WeatherData[] {
    const data = localStorage.getItem('weatherData');
    return data ? JSON.parse(data) : [];
}

// Function to set data to local storage
function setDataToLocalStorage(data: WeatherData[]) {
    console.log(weatherData);
    localStorage.setItem('weatherData', JSON.stringify(data));
}

// Delete weather data from array and local storage
function deleteWeatherData(index: number) {
    weatherData.splice(index, 1);
    localStorage.setItem('weatherData', JSON.stringify(weatherData));
}

// Load the data from local storage on mounted
onMounted(() => {
    const savedData = loadDataFromLocalStorage();
    if (savedData.length > 0) {
        weatherData.splice(0, weatherData.length, ...savedData);
    }

    if (props.data && !props.data.error) {
        weatherData.push(props.data);
        setDataToLocalStorage(weatherData);
    } else if (props.data && props.data.error) {
        allRefs.value.errorMessage = props.data.error.message;
        console.log(allRefs.value.errorMessage);
    }
});

// Define a method to send the GET request to the backend
async function submit() {
    try {
        await router.get('/weather',  { searchWeather: allRefs.value.searchWeather });
    } catch (error) {
        console.log(error);
    }
}
</script>

Controller:

<?php

declare(strict_types=1);

namespace App\Http\Controllers;
use App\Classes\ApiErrorCodes;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Inertia\Inertia;
use Illuminate\Support\Facades\Validator;

class GetWeather extends Controller
{

    public function fetchWeather(Request $request): object
    {

        $city = $request->input('searchWeather');
        $key = config('services.weather.key');

        $response = Http::get(
            url: "https://api.weatherapi.com/v1/forecast.json",
            query: [
                'key' => $key,
                'q' => $city,
                'days' => 3,
                'aqi' => 'no',
                'alerts' => 'no',
                'lang' => 'de',
            ]
        );

        // Decode the JSON content of the HTTP response into a PHP array
        $data = json_decode($response->body(), true);

        // Checks the array for type declaration
        $validator = Validator::make($data, [
            'current.cloud' => 'integer',
            'current.condition.text' => 'string',
            'current.condition.icon' => 'string',
            'current.is_day' => 'integer',
            'current.last_updated_epoch' => 'integer',
            'current.temp_c' => 'numeric',
            'current.uv' => 'numeric',
            'current.wind_kph' => 'numeric',
            'forecast.forecastday' => 'array',
            'location.country' => 'string',
            'location.lat' => 'numeric',
            'location.localtime' => 'string',
            'location.localtime_epoch' => 'integer',
            'location.lon' => 'numeric',
            'location.name' => 'string',
            'location.region' => 'string',
            'location.tz_id' => 'string',
        ]);

        // Check if there is an error code in the JSON response
        if (isset($data['error']) && isset($data['error']['code'])) {
            // Extract error code from JSON response
            $errorCode = $data['error']['code'];

            // Define array of valid error codes
            $validErrorCodes = [
                ApiErrorCodes::CODE_API_KEY_INVALID,
                ApiErrorCodes::CODE_API_KEY_DISABLED,
                ApiErrorCodes::CODE_NO_MATCHING_LOCATION,
                ApiErrorCodes::CODE_PARAMETER_MISSING
            ];

            // Check if the error code is included in the list of valid error codes
            if (in_array($errorCode, $validErrorCodes)) {
                // Fehlermeldung in der JSON-Antwort aktualisieren
                $data['error']['message'] = ApiErrorCodes::getMessage($errorCode);

            }

        }

        // Checks if an error occurred during validation
        if ($validator->fails()) {
            // Validation error
            return response()->json(['error' => $validator->errors()], 422);
        }


        //Calls the template and passes the object
        return Inertia::render('Mainpage', ['data' => $data]);

    }

}

Interface:

export interface WeatherData {
    error: {
        message: string;
    };
    current: {
        cloud: number;
        condition: {
            text: string;
            icon: string;
        };
        is_day: number;
        last_updated_epoch: number;
        temp_c: number;
        uv: number;
        wind_kph: number;
    };
    forecast: {
        forecastday: {
            date: string;
            day: {
                maxtemp_c: number;
                daily_chance_of_rain: number;
                condition: {
                    text: string;
                    icon: string;
                };
            };
        }[];
    };
    location: {
        country: string;
        lat: number;
        localtime: string;
        localtime_epoch: number;
        lon: number;
        name: number;
        region: string;
        tz_id: string;
    };
}
  • TS types don't exist at runtime. You assert that a response confirms WeatherData interface, not validate it. – Estus Flask May 10 '23 at 06:29
  • Hello #EstusFlask. Thank you for your quick reply. Is my code correct, because I don't get any errors from the console? – Michael Brilz May 10 '23 at 06:35
  • Yes, your code is correct. And yes, you won't get errors in console. If you really need to check data at runtime you'll need a schema validation lib. – Estus Flask May 10 '23 at 09:48
  • Possible duplicate of https://stackoverflow.com/questions/44078205/how-to-check-the-object-type-on-runtime-in-typescript – Estus Flask May 10 '23 at 09:48

0 Answers0