ask-sdk-jsx-for-aplを使って、TypeScriptでAlexa APLを作る

AlexaLive 2020でask-sdk-jsx-for-aplが発表され、APLをついにReactライクに作れるようになりました。 Reactめっちゃ書いてるマンとしては見逃せないので、早速触ってみます。 Inst […]

広告ここから
広告ここまで

目次

    AlexaLive 2020でask-sdk-jsx-for-aplが発表され、APLをついにReactライクに作れるようになりました。

    Reactめっちゃ書いてるマンとしては見逃せないので、早速触ってみます。

    Install

    以下の3ライブラリを追加します。

    $ npm install -S ask-sdk-jsx-for-apl react react-dom

    また、TypeScriptでReactを使うには@typesも必要なので追加しましょう。

    $ npm install -D @types/react @types/react-dom

    Check and update tsconfig.json

    tsconfig.jsonの設定が以下のようになっているか確認しましょう。

    • compilerOptions.jsx = 'react'
    • compilerOptions.esModuleInterop = true

    こんな感じになっていればとりあえずOKです。

    {
        "compilerOptions": {
          "module": "commonjs",
          "target": "es6",
          "noImplicitAny": true,
          "noImplicitReturns": true,
          "noFallthroughCasesInSwitch": true,
          "outDir": "./dist",
          "esModuleInterop": true,
          "jsx": "react",
          "allowJs": false
        },
        "include": [
            "./src/**/*"
        ]
      }

    Create APL by React

    ということで早速APLを書いていきます。

    import React from 'react';
    import { APL, MainTemplate, Container, Text } from 'ask-sdk-jsx-for-apl';
    
    /**
     * Class component
     */
    export class LaunchAplDocument extends React.Component {
        private readonly launchMessage = 'Welcome to my first JSX for APL skill!';
        render() {
            return (
                <APL theme="dark">
                    <MainTemplate>
                        <Container
                            alignItems="center"
                            justifyContent="spaceAround">
                            <Text
                                text={this.launchMessage}
                                fontSize="50px"
                                color="rgb(251,184,41)" />
                        </Container>
                    </MainTemplate>
                </APL>
            );
        }
    }
    
    /**
     * Functional Component
     */
    export const LaunchAplDocumentFC: React.FC = () => {
        const launchMessage = 'Welcome to my first JSX for APL skill!';
        return (
            <APL theme="dark">
                <MainTemplate>
                    <Container
                        alignItems="center"
                        justifyContent="spaceAround">
                        <Text
                            text={launchMessage}
                            fontSize="50px"
                            color="rgb(251,184,41)" />
                    </Container>
                </MainTemplate>
            </APL>
        );
    }
    

    作成したAPLは、AplDocumentクラスを利用してaddDirectiveします。

    import React from 'react'
    import { RequestHandler } from 'ask-sdk-core';
    import { AplDocument } from 'ask-sdk-jsx-for-apl';
    import { LaunchAplDocument } from './LaunchRequest.apl';
    
    export const LaunchRequestHandler:  RequestHandler = {
        async canHandle(handlerInput) {
            return handlerInput.requestEnvelope.request.type === "LaunchRequest"
        },
        async handle(handlerInput) {
            return handlerInput.responseBuilder
                .speak("Hello! It's a nice development. How are you?")
                .reprompt("How are you?")
                .addDirective(
                    new AplDocument(
                        <LaunchAplDocument />
                    ).getDirective()
                )
                .getResponse()        
        }
    }
    
    export default LaunchRequestHandler

    Trouble shooting

    Error: TS2352: Conversion of type ‘RegExp’ to type ‘LaunchAplDocument’ may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to ‘unknown’ first.

    .tsxでJSXを書いていない場合、このエラーに遭遇します。

    .ts ファイルを必要に応じて .tsxにリネームしましょう。

    Left: LaunchRequest.ts (get Error)
    Right: LaunchRequest.tsx (No Error)

    Error: TS2686: ‘React’ refers to a UMD global, but the current file is a module. Consider adding an import instead.

    JSXが記述されているファイルでは、必ずimport React from 'react'しましょう。

    Left: import React (No Error)
    Right:nothing (Get Error)

    Test your APL Component by Jest

    Reactなので、もちろんJestでテストできます。

    import React from 'react';
    import {
        APL,
        MainTemplate,
        Container,
        Text,
        AplDocument
    } from 'ask-sdk-jsx-for-apl'
    
    const LaunchAplDocumentFC: React.FC = () => {
        const launchMessage = 'Welcome to my first JSX for APL skill!';
        return (
            <APL theme="dark">
                <MainTemplate>
                    <Container
                        alignItems="center"
                        justifyContent="spaceAround">
                        <Text
                            text={launchMessage}
                            fontSize="50px"
                            color="rgb(251,184,41)" />
                    </Container>
                </MainTemplate>
            </APL>
        );
    }
    
    describe('AplDocument', () => {
        const directive = new AplDocument(<LaunchAplDocumentFC />).getDirective()
        it('should get RenderDocument Directive', () => {
            expect(directive).toEqual({
                "document": {
                  "import": [],
                  "mainTemplate": {
                    "items": [
                      {
                        "alignItems": "center",
                        "items": [
                          {
                            "color": "rgb(251,184,41)",
                            "fontSize": "50px",
                            "items": [],
                            "text": "Welcome to my first JSX for APL skill!",
                            "type": "Text"
                          }
                        ],
                        "justifyContent": "spaceAround",
                        "type": "Container"
                      }
                    ],
                    "parameters": []
                  },
                  "theme": "dark",
                  "type": "APL",
                  "version": "1.3"
                },
                "type": "Alexa.Presentation.APL.RenderDocument"
              })
        })
    })

    Use snapshot testing to check the component update

    Jestには戻り値が変わったかどうかを検知できるスナップショットテストがあります。

    import React from 'react';
    import {
        APL,
        MainTemplate,
        Container,
        Text,
        AplDocument
    } from 'ask-sdk-jsx-for-apl'
    
    const LaunchAplDocumentFC: React.FC = () => {
        const launchMessage = 'Welcome to my first JSX for APL skill!';
        return (
            <APL theme="dark">
                <MainTemplate>
                    <Container
                        alignItems="center"
                        justifyContent="spaceAround">
                        <Text
                            text={launchMessage}
                            fontSize="50px"
                            color="rgb(251,184,41)" />
                    </Container>
                </MainTemplate>
            </APL>
        );
    }
    
    describe('AplDocument', () => {
        const directive = new AplDocument(<LaunchAplDocumentFC />).getDirective()
        it('should match snapshot', () => {
            expect(directive).toMatchSnapshot()
        })
    })

    一度テストを実行してスナップショットを作っておきます。

    その後、Textのcolorをrgb(251,184,41) から rgb(255,184,41)に変更してみると、以下のようにJestでエラーがでます。

    
        Snapshot name: `AplDocument should match snapshot 1`
    
        - Snapshot  - 1
        + Received  + 1
    
        @@ -14,11 +14,11 @@
                "items": Array [
                  Object {
                    "alignItems": "center",
                    "items": Array [
                      Object {
        -               "color": "rgb(251,184,41)",
        +               "color": "rgb(255,184,41)",
                        "fontSize": "50px",
                        "items": Array [],
                        "text": "Welcome to my first JSX for APL skill!",
                        "type": "Text",
                      },
    
          29 |     const directive = new AplDocument(<LaunchAplDocumentFC />).getDirective()
          30 |     it('should match snapshot', () => {
        > 31 |         expect(directive).toMatchSnapshot()
             |                           ^
          32 |     })
          33 |     it('should get RenderDocument Directive', () => {

    想定している変更であれば、-uオプションを使ってスナップショットテストを更新しましょう。もしそうでない場合は・・・バグなので要修正です。

    広告ここから
    広告ここまで
    Home
    Search
    Bookmark