As mentioned in the comment about Ansible Issue #12519 it seems to be a language feature of YAML. According Ansible set_fact type cast one could set DEFAULT_JINJA2_NATIVE
---
- hosts: localhost
become: false
gather_facts: false
vars:
yes1: yes
yes2: 'yes'
yes3: "yes"
tasks:
- name: Set yes
set_fact:
yes4: 'yes'
yes5: "yes"
- name: Show values
debug:
msg: |
yes1 is {{ yes1 | type_debug }} and has value {{ yes1 }}
yes2 is {{ yes2 | type_debug }} and has value {{ yes2 }}
yes3 is {{ yes3 | type_debug }} and has value {{ yes3 }}
yes4 is {{ yes4 | type_debug }} and has value {{ yes4 }}
yes5 is {{ yes5 | type_debug }} and has value {{ yes5 }}
resulting into an output of
ANSIBLE_JINJA2_NATIVE=True ansible-playbook set_yes_string.yml
[WARNING]: jinja2_native requires Jinja 2.10 and above. Version detected: 2.7.2. Falling back to default.
...
TASK [Show values] *************************
ok: [localhost] =>
msg: |-
yes1 is bool and has value True
yes2 is AnsibleUnicode and has value yes
yes3 is AnsibleUnicode and has value yes
yes4 is AnsibleUnicode and has value yes
yes5 is AnsibleUnicode and has value yes
Furthermore, just only setting a bool to string filter will not work.